RCSID("$Header$");
-extern char buf1[];
extern int nclients;
+extern client **clients;
extern char *whoami;
extern int dbms_errno, mr_errcode;
-static int row_count;
void do_call(client *cl);
void free_rtn_tuples(client *cp);
-int retr_callback(int argc, char **argv, void *p_cp);
-int list_users(int (*callbk)(int, char **, void *), char *callarg);
-void do_retr(client *cl);
-void do_access(client *cl);
+int retr_callback(int argc, char **argv, void *p_cl);
+int list_users(client *cl);
+void do_retr(client *cl, mr_params req);
+void do_access(client *cl, mr_params req);
void get_motd(client *cl);
/* Put this in a variable so that we can patch it if necessary */
int max_row_count = 4096;
-/*
- * Welcome to the (finite state) machine (highest level).
- */
-void do_client(client *cp)
-{
- struct stat stbuf;
-
- free_rtn_tuples(cp);
- if (OP_STATUS(cp->pending_op) == OP_CANCELLED)
- {
- com_err(whoami, 0,
- "Closed connection (now %d client%s, %d new queries, %d old)",
- nclients - 1, nclients != 2 ? "s" : "", newqueries, oldqueries);
- clist_delete(cp);
- /* if we no longer have any clients, and we're supposed to
- * go down, then go down now.
- */
- if ((dormant == AWAKE) && (nclients == 0) &&
- (stat(MOIRA_MOTD_FILE, &stbuf) == 0))
- {
- com_err(whoami, 0, "motd file exists, slumbertime");
- dormant = SLEEPY;
- }
- return;
- }
- switch (cp->action)
- {
- case CL_ACCEPT:
- case CL_SEND:
- /* Start recieving next request */
- initialize_operation(cp->pending_op, mr_start_recv,
- (char *)&cp->args, NULL);
- queue_operation(cp->con, CON_INPUT, cp->pending_op);
- cp->action = CL_RECEIVE;
- break;
- case CL_RECEIVE:
- /* Data is here. Process it & start it heading back */
- do_call(cp); /* This may block for a while. */
- mr_destroy_reply(cp->args);
- cp->args = NULL;
- initialize_operation(cp->pending_op, mr_start_send,
- (char *)&cp->reply, NULL);
- queue_operation(cp->con, CON_OUTPUT, cp->pending_op);
- cp->action = CL_SEND;
- break;
- }
-}
-
char *procnames[] = {
"noop",
"auth",
"motd",
};
+int newqueries;
-void do_call(client *cl)
+void client_read(client *cl)
{
- int pn;
- cl->reply.mr_argc = 0;
- cl->reply.mr_status = 0;
- cl->reply.mr_version_no = cl->args->mr_version_no;
- if (((pn = cl->args->mr_procno) < 0) || (pn > MR_MAX_PROC))
+ mr_params req;
+ int status, pn;
+
+ status = mr_receive(cl->con, &req);
+ if (status != MR_SUCCESS)
{
- com_err(whoami, 0, "procno out of range");
- cl->reply.mr_status = MR_UNKNOWN_PROC;
+ cl->done = 1;
+ if (status != MR_NOT_CONNECTED)
+ com_err(whoami, status, "while reading from socket");
return;
}
- if (log_flags & LOG_ARGS)
+
+ pn = req.u.mr_procno;
+ if (pn < 0 || pn > MR_MAX_PROC)
{
- log_args(procnames[pn], cl->args->mr_version_no,
- cl->args->mr_argc, cl->args->mr_argv);
+ com_err(whoami, 0, "procno out of range");
+ client_reply(cl, MR_UNKNOWN_PROC);
+ return;
}
- else if (log_flags & LOG_REQUESTS)
- com_err(whoami, 0, "%s", procnames[pn]);
+ log_args(procnames[pn], 2, req.mr_argc, req.mr_argv);
- if ((dormant == ASLEEP || dormant == GROGGY) &&
- pn != MR_NOOP && pn != MR_MOTD)
+ if (dormant == ASLEEP && pn != MR_NOOP && pn != MR_MOTD)
{
- cl->reply.mr_status = MR_DOWN;
- if (log_flags & LOG_RES)
- com_err(whoami, MR_DOWN, "(query refused)");
+ client_reply(cl, MR_DOWN);
+ com_err(whoami, MR_DOWN, "(query refused)");
return;
}
switch (pn)
{
case MR_NOOP:
- cl->reply.mr_status = 0;
+ client_reply(cl, MR_SUCCESS);
return;
case MR_AUTH:
- do_auth(cl);
+ do_auth(cl, req);
return;
case MR_QUERY:
- do_retr(cl);
+ do_retr(cl, req);
return;
case MR_ACCESS:
- do_access(cl);
+ do_access(cl, req);
return;
case MR_SHUTDOWN:
return;
case MR_DO_UPDATE:
- trigger_dcm(NULL, NULL, cl);
+ client_reply(cl, MR_PERM);
return;
case MR_MOTD:
}
}
-void free_rtn_tuples(client *cp)
+/* Set the final return status for a query. We always keep one more
+ free slot in cl->tuples[] than we're using so that this can't fail */
+void client_reply(client *cl, long status)
{
- returned_tuples *temp;
- for (temp = cp->first; temp && OP_DONE(temp->op); )
- {
- returned_tuples *t1 = temp;
- temp = t1->next;
- if (t1 == cp->last)
- cp->last = NULL;
-
- mr_destroy_reply(t1->retval);
- delete_operation(t1->op);
- free(t1);
- }
- cp->first = temp;
+ cl->tuples[cl->ntuples].u.mr_status = status;
+ cl->tuples[cl->ntuples].mr_argc = 0;
+ cl->tuples[cl->ntuples].mr_argl = NULL;
+ cl->tuples[cl->ntuples].mr_argv = NULL;
+ cl->ntuples++;
}
-int retr_callback(int argc, char **argv, void *p_cp)
+void client_return_tuple(client *cl, int argc, char **argv)
{
- client *cp = p_cp;
- mr_params *arg_tmp;
- returned_tuples *tp;
- OPERATION op_tmp;
- char **nargv;
- int i;
+ if (cl->done || dbms_errno)
+ return;
- if (row_count++ >= max_row_count)
+ if (cl->ntuples == max_row_count)
{
dbms_errno = mr_errcode = MR_NO_MEM;
- return MR_ABORT;
+ return;
}
- /*
- * This takes too much advantage of the fact that
- * serialization of the data happens during the queue operation.
- */
- arg_tmp = malloc(sizeof(mr_params));
- tp = malloc(sizeof(returned_tuples));
- nargv = malloc(argc * sizeof(char *));
-
- op_tmp = create_operation();
-
- if (mr_trim_args(argc, argv) == MR_NO_MEM)
- com_err(whoami, MR_NO_MEM, "while trimming args");
- if (log_flags & LOG_RESP)
- log_args("return: ", cp->args->mr_version_no, argc, argv);
-
- tp->op = op_tmp;
- tp->retval = arg_tmp;
- tp->next = NULL;
-
- arg_tmp->mr_status = MR_MORE_DATA;
- arg_tmp->mr_version_no = cp->args->mr_version_no;
- arg_tmp->mr_argc = argc;
- arg_tmp->mr_argv = nargv;
- for (i = 0; i < argc; i++)
+ if (cl->ntuples == cl->tuplessize - 1)
{
- int len = strlen(argv[i]) + 1;
- nargv[i] = malloc(len);
- memcpy(nargv[i], argv[i], len);
+ int newsize = (cl->tuplessize + 4) * 2;
+ mr_params *newtuples;
+
+ newtuples = realloc(cl->tuples, newsize * sizeof(mr_params));
+ if (!newtuples)
+ {
+ free_rtn_tuples(cl);
+ dbms_errno = mr_errcode = MR_NO_MEM;
+ return;
+ }
+ cl->tuplessize = newsize;
+ cl->tuples = newtuples;
}
- arg_tmp->mr_flattened = NULL;
- arg_tmp->mr_argl = NULL;
- if (cp->last)
- {
- cp->last->next = tp;
- cp->last = tp;
- } else
- cp->last = cp->first = tp;
-
- reset_operation(op_tmp);
- initialize_operation(op_tmp, mr_start_send, (char *)arg_tmp, NULL);
- queue_operation(cp->con, CON_OUTPUT, op_tmp);
- return MR_CONT;
+ cl->tuples[cl->ntuples].u.mr_status = MR_MORE_DATA;
+ cl->tuples[cl->ntuples].mr_argc = argc;
+ cl->tuples[cl->ntuples].mr_argl = NULL;
+ cl->tuples[cl->ntuples].mr_argv = mr_copy_args(argv, argc);
+ cl->ntuples++;
}
-int list_users(int (*callbk)(int, char **, void *), char *callarg)
+void client_write(client *cl)
{
- char *argv[6];
- char buf[30];
- char buf1[30];
- int i;
- extern client **clients;
- char *cp;
+ int status;
- for (i = 0; i < nclients; i++)
+ status = mr_send(cl->con, &cl->tuples[cl->nexttuple]);
+ if (status)
{
- client *cl = clients[i];
- if (cl->clname)
- argv[0] = cl->clname;
- else argv[0] = "unauthenticated";
-
- argv[1] = inet_ntoa(cl->haddr.sin_addr);
- argv[2] = buf;
- sprintf(buf, "port %d", ntohs(cl->haddr.sin_port));
- argv[3] = ctime(&cl->last_time_used);
- cp = strchr(argv[3], '\n');
- if (cp)
- *cp = '\0';
- argv[4] = buf1;
- sprintf(buf1, "[#%d]", cl->id);
- (*callbk)(5, argv, callarg);
+ com_err(whoami, status, "writing to socket");
+ cl->done = 1;
+ }
+ else
+ {
+ cl->nexttuple++;
+ if (cl->nexttuple == cl->ntuples)
+ free_rtn_tuples(cl);
}
- return 0;
}
-void do_retr(client *cl)
+void free_rtn_tuples(client *cl)
{
- char *queryname;
+ for (cl->ntuples--; cl->ntuples >= 0; cl->ntuples--)
+ free_argv(cl->tuples[cl->ntuples].mr_argv,
+ cl->tuples[cl->ntuples].mr_argc);
+ free(cl->tuples);
+
+ cl->tuples = xmalloc(sizeof(mr_params));
+ cl->tuplessize = 1;
+ cl->ntuples = cl->nexttuple = 0;
+}
- cl->reply.mr_argc = 0;
- cl->reply.mr_status = 0;
- row_count = 0;
+void do_retr(client *cl, mr_params req)
+{
+ char *queryname;
+ int status;
- if (cl->args->mr_argc < 1)
+ if (req.mr_argc < 1)
{
- cl->reply.mr_status = MR_ARGS;
+ client_reply(cl, MR_ARGS);
com_err(whoami, MR_ARGS, "got nameless query");
return;
}
- queryname = cl->args->mr_argv[0];
-
- if (cl->args->mr_version_no == MR_VERSION_2)
- newqueries++;
- else
- oldqueries++;
+ queryname = req.mr_argv[0];
+ newqueries++;
if (!strcmp(queryname, "_list_users"))
- cl->reply.mr_status = list_users(retr_callback, (char *)cl);
+ status = list_users(cl);
else
+ status = mr_process_query(cl, queryname, req.mr_argc - 1, req.mr_argv + 1,
+ retr_callback, cl);
+
+ client_reply(cl, status);
+
+ if (cl->ntuples >= max_row_count)
{
- cl->reply.mr_status = mr_process_query(cl, queryname,
- cl->args->mr_argc - 1,
- cl->args->mr_argv + 1,
- retr_callback, cl);
- }
- if (row_count >= max_row_count)
- {
- critical_alert("moirad", "attempted query %s with %d rows\n",
- queryname, row_count);
+ critical_alert("moirad", "attempted query %s with too many rows\n",
+ queryname);
}
- if (log_flags & LOG_RES)
- com_err(whoami, 0, "Query complete.");
+ com_err(whoami, 0, "Query complete.");
+}
+
+int retr_callback(int argc, char **argv, void *p_cl)
+{
+ client *cl = p_cl;
+
+ mr_trim_args(argc, argv);
+ client_return_tuple(cl, argc, argv);
}
-void do_access(client *cl)
+void do_access(client *cl, mr_params req)
{
- if (cl->args->mr_argc < 1)
+ int status;
+
+ if (req.mr_argc < 1)
{
- cl->reply.mr_status = MR_ARGS;
+ client_reply(cl, MR_ARGS);
com_err(whoami, MR_ARGS, "got nameless access");
return;
}
- cl->reply.mr_argc = 0;
- cl->reply.mr_status = mr_check_access(cl, cl->args->mr_argv[0],
- cl->args->mr_argc - 1,
- cl->args->mr_argv + 1);
+ status = mr_check_access(cl, req.mr_argv[0], req. mr_argc - 1,
+ req.mr_argv + 1);
+ client_reply(cl, status);
com_err(whoami, 0, "Access check complete.");
}
-
-/* trigger_dcm is also used as a followup routine to the
- * set_server_host_override query, hence the two dummy arguments.
- */
-
-struct query pseudo_query = {
- "trigger_dcm",
- "tdcm",
-};
-
-int trigger_dcm(struct query *q, char *argv[], client *cl)
+void get_motd(client *cl)
{
- int pid;
- char prog[128];
-
- cl->reply.mr_argc = 0;
-
- if ((cl->reply.mr_status = check_query_access(&pseudo_query, 0, cl)))
- return cl->reply.mr_status;
+ int motd;
+ char *buffer;
+ struct stat statb;
- sprintf(prog, "%s/startdcm", BIN_DIR);
- pid = vfork();
- switch (pid)
+ if (stat(MOIRA_MOTD_FILE, &statb) == -1)
{
- case 0:
- execl(prog, "startdcm", 0);
- exit(1);
-
- case -1:
- cl->reply.mr_status = errno;
- return 0;
+ client_reply(cl, MR_SUCCESS);
+ return;
+ }
+
+ buffer = malloc(statb.st_size + 1);
+ if (!buffer)
+ {
+ client_reply(cl, MR_NO_MEM);
+ return;
+ }
- default:
- return 0;
+ motd = open(MOIRA_MOTD_FILE, 0, O_RDONLY);
+ if (motd)
+ {
+ read(motd, buffer, statb.st_size);
+ close(motd);
+ buffer[statb.st_size] = '\0';
+ client_return_tuple(cl, 1, &buffer);
+ client_reply(cl, MR_SUCCESS);
}
-}
+ else
+ client_reply(cl, errno);
+ free(buffer);
+}
-void get_motd(client *cl)
+int list_users(client *cl)
{
- int motd, len;
- char buffer[1024];
- char *arg[1];
+ char *argv[5];
+ char buf[30];
+ char buf1[30];
+ int i;
+ char *cp;
- arg[0] = buffer;
- cl->reply.mr_status = 0;
- motd = open(MOIRA_MOTD_FILE, 0, O_RDONLY);
- if (motd < 0)
- return;
- len = read(motd, buffer, sizeof(buffer) - 1);
- close(motd);
- buffer[len] = 0;
- row_count = 0;
- retr_callback(1, arg, cl);
- cl->reply.mr_status = 0;
+ for (i = 0; i < nclients; i++)
+ {
+ client *c = clients[i];
+ argv[0] = c->clname;
+ argv[1] = inet_ntoa(c->haddr.sin_addr);
+ argv[2] = buf;
+ sprintf(buf, "port %d", ntohs(c->haddr.sin_port));
+ argv[3] = ctime(&c->last_time_used);
+ cp = strchr(argv[3], '\n');
+ if (cp)
+ *cp = '\0';
+ argv[4] = buf1;
+ sprintf(buf1, "[#%d]", c->id);
+ client_return_tuple(cl, 5, argv);
+ }
+ return MR_SUCCESS;
}
+