summaryrefslogtreecommitdiffabout
path: root/src/server.c
Side-by-side diff
Diffstat (limited to 'src/server.c') (more/less context) (ignore whitespace changes)
-rw-r--r--src/server.c193
1 files changed, 144 insertions, 49 deletions
diff --git a/src/server.c b/src/server.c
index 82aae0e..e514f37 100644
--- a/src/server.c
+++ b/src/server.c
@@ -37,8 +37,11 @@
#include <tagr.h>
#include <report.h>
#include <inttypes.h>
+#include <c-strcase.h>
#include <wordsplit.h>
+unsigned stream_idle_timeout = 10;
+
struct tagr_server
{
struct tagr_server *next;
@@ -48,7 +51,7 @@ struct tagr_server
struct grecs_sockaddr addr;
};
-struct tagr_server *server_head, *server_tail;
+static struct tagr_server *server_head, *server_tail;
struct server_class
{
@@ -194,8 +197,9 @@ subprocess (void (*fun) (void *data), void *data, int fd)
logmsg (L_ERR, _("cannot fork: %s"), strerror (errno));
else
{
- signal (SIGHUP, SIG_IGN);
- signal (SIGCHLD, SIG_IGN);
+ signal (SIGHUP, SIG_DFL);
+ signal (SIGCHLD, SIG_DFL);
+ signal (SIGALRM, SIG_DFL);
child = 1;
if (fd != -1)
close (fd);
@@ -302,7 +306,7 @@ tcp_open (struct tagr_server *srv)
return 0;
}
-static void
+void
trim_crlf (char *buf)
{
size_t len = strlen (buf);
@@ -344,75 +348,166 @@ convert_ulong (const char *str, unsigned long *pt)
return 0;
}
+static RETSIGTYPE
+sig_child_alrm (int sig)
+{
+ logmsg (L_NOTICE, _("child timed out"));
+ exit (EX_TEMPFAIL);
+}
+
+enum tagr_dialog_state
+ {
+ initial_dialog_state,
+ auth_dialog_state,
+ quit_dialog_state
+ };
+
+struct tagr_dialog
+{
+ enum tagr_dialog_state state;
+ FILE *in;
+ FILE *out;
+};
+
+#define STATEMASK(n) (1u<<(n))
+#define ANYSTATE ((unsigned)~0)
+
+struct command
+{
+ const char *name;
+ unsigned statemask;
+ int minargs;
+ int maxargs;
+ void (*handler) (struct tagr_dialog *, struct command *,
+ struct wordsplit *);
+};
+
+static void
+cmd_auth (struct tagr_dialog *dlg, struct command *cmd,
+ struct wordsplit *ws)
+{
+ if (tagr_auth (ws->ws_wordv[1], ws->ws_wordv[2]) == 0)
+ {
+ dlg->state = auth_dialog_state;
+ fprintf (dlg->out, "+OK welcome, %s\r\n", ws->ws_wordv[1]);
+ }
+ else
+ fprintf (dlg->out, "-ERR authorization failed\r\n");
+}
+
+static void
+cmd_sample (struct tagr_dialog *dlg, struct command *cmd,
+ struct wordsplit *ws)
+{
+ Stat st;
+ time_t t;
+
+ if (strlen (ws->ws_wordv[1]) > MAX_NAME_LENGTH)
+ {
+ fprintf (dlg->out, "-ERR id too long\r\n");
+ return;
+ }
+
+ strcpy (st.name, ws->ws_wordv[1]);
+ if (convert_ts (ws->ws_wordv[2], &t)
+ || convert_ulong (ws->ws_wordv[3], &st.in)
+ || convert_ulong (ws->ws_wordv[4], &st.out))
+ {
+ fprintf (dlg->out, "-ERR invalid input\r\n");
+ return;
+ }
+
+ if (open_db (TAGR_DB_WR))
+ fprintf (dlg->out, "-TEMP database not available\r\n");
+ else
+ {
+ report (&st, t);
+ close_db ();
+ fprintf (dlg->out, "+OK thank you\r\n");
+ }
+}
+
+static void
+cmd_quit (struct tagr_dialog *dlg, struct command *cmd,
+ struct wordsplit *ws)
+{
+ dlg->state = quit_dialog_state;
+ fprintf (dlg->out, "+OK bye\r\n");
+}
+
+static struct command command_tab[] = {
+ { "AUTH", STATEMASK (initial_dialog_state), 3, 3, cmd_auth },
+ { "SAMPLE", STATEMASK (auth_dialog_state), 5, 5, cmd_sample },
+ { "QUIT", ANYSTATE, 1, 1, cmd_quit },
+ { NULL }
+};
+
+static struct command *
+find_command (const char *name)
+{
+ struct command *cmd;
+
+ for (cmd = command_tab; cmd->name; cmd++)
+ if (c_strcasecmp (cmd->name, name) == 0)
+ return cmd;
+ return NULL;
+}
+
+
static void
tcp_server_loop (void *data)
{
int fd = *(int*)data;
- FILE *in = fdopen (fd, "r");
- FILE *out = fdopen (fd, "w");
+ struct tagr_dialog dlg;
size_t bufsize = 0;
char *buf = NULL;
- int quit = 0;
-
- setvbuf (in, NULL, _IOLBF, 0);
- setvbuf (out, NULL, _IOLBF, 0);
+ const char *authid;
+
+ signal (SIGALRM, sig_child_alrm);
- fprintf (out, "+OK tagr experimental stream service\r\n");
- while (!quit && getline (&buf, &bufsize, in) > 0)
+ dlg.state = initial_dialog_state;
+ dlg.in = fdopen (fd, "r");
+ dlg.out = fdopen (fd, "w");
+ setvbuf (dlg.in, NULL, _IOLBF, 0);
+ setvbuf (dlg.out, NULL, _IOLBF, 0);
+
+ authid = tagr_auth_init ();
+ fprintf (dlg.out, "+OK tagr experimental stream service %s\r\n", authid);
+ while (dlg.state != quit_dialog_state
+ && getline (&buf, &bufsize, dlg.in) > 0)
{
struct wordsplit ws;
-
+ struct command *cmd;
+
+ alarm (stream_idle_timeout);
trim_crlf (buf);
if (wordsplit (buf, &ws, WRDSF_DEFFLAGS))
{
- logmsg (L_ERR, "failed to parse input line");
- fprintf (out, "-ERR parsing failed\r\n");
+ logmsg (L_ERR, _("failed to parse input line"));
+ fprintf (dlg.out, "-ERR parsing failed\r\n");
continue;
}
if (ws.ws_wordc == 0)
- fprintf (out, "-ERR invalid input\r\n");
- else if (strcasecmp (ws.ws_wordv[0], "SAMPLE") == 0
- && ws.ws_wordc == 5)
+ fprintf (dlg.out, "-ERR invalid input\r\n");
+ else if (cmd = find_command (ws.ws_wordv[0]))
{
- Stat st;
- time_t t;
-
- if (strlen (ws.ws_wordv[1]) > MAX_NAME_LENGTH)
- fprintf (out, "-ERR id too long\r\n");
+ if (!(STATEMASK (dlg.state) & cmd->statemask))
+ fprintf (dlg.out, "-ERR wrong state\r\n");
+ else if (ws.ws_wordc < cmd->minargs
+ || (cmd->maxargs > 0 && ws.ws_wordc > cmd->maxargs))
+ fprintf (dlg.out, "-ERR invalid number of arguments\r\n");
else
- {
- strcpy (st.name, ws.ws_wordv[1]);
- if (convert_ts (ws.ws_wordv[2], &t)
- || convert_ulong (ws.ws_wordv[3], &st.in)
- || convert_ulong (ws.ws_wordv[4], &st.out))
- fprintf (out, "-ERR invalid input\r\n");
- else
- {
- if (open_db (TAGR_DB_WR))
- fprintf (out, "-ERR database not available\r\n");
- else
- {
- report (&st, t);
- close_db ();
- fprintf (out, "+OK thank you\r\n");
- }
- }
- }
- }
- else if (strcasecmp (ws.ws_wordv[0], "QUIT") == 0)
- {
- quit = 1;
- fprintf (out, "+OK bye\r\n");
+ cmd->handler (&dlg, cmd, &ws);
}
else
- fprintf (out, "-ERR invalid input\r\n");
+ fprintf (dlg.out, "-ERR invalid input\r\n");
wordsplit_free (&ws);
}
free (buf);
- fclose (in);
- fclose (out);
+ fclose (dlg.in);
+ fclose (dlg.out);
close (fd);
}

Return to:

Send suggestions and report system problems to the System administrator.