Trivial
[pazpar2-moved-to-github.git] / command.c
1 /* $Id: command.c,v 1.5 2006-12-03 06:43:24 quinn Exp $ */
2
3 #include <stdio.h>
4 #include <sys/socket.h>
5 #include <sys/types.h>
6 #include <sys/uio.h>
7 #include <unistd.h>
8 #include <stdlib.h>
9 #include <strings.h>
10 #include <ctype.h>
11 #include <fcntl.h>
12
13 #include <yaz/yaz-util.h>
14 #include <yaz/comstack.h>
15 #include <netdb.h>
16
17 #include "command.h"
18 #include "util.h"
19 #include "eventl.h"
20 #include "pazpar2.h"
21
22 extern IOCHAN channel_list;
23
24 struct command_session {
25     IOCHAN channel;
26     char *outbuf;
27
28     int outbuflen;
29     int outbufwrit;
30
31     struct session *psession;
32 };
33
34 void command_destroy(struct command_session *s);
35 void command_prompt(struct command_session *s);
36 void command_puts(struct command_session *s, const char *buf);
37
38 static int cmd_quit(struct command_session *s, char **argv, int argc)
39 {
40     IOCHAN i = s->channel;
41     close(iochan_getfd(i));
42     iochan_destroy(i);
43     command_destroy(s);
44     return 0;
45 }
46
47 #ifdef GAGA
48 static int cmd_load(struct command_session *s, char **argv, int argc)
49 {
50     if (argc != 2) {
51         command_puts(s, "Usage: load filename\n");
52     }
53     if (load_targets(s->psession, argv[1]) < 0)
54         command_puts(s, "Failed to open file\n");
55     return 1;
56 }
57 #endif
58
59 static int cmd_search(struct command_session *s, char **argv, int argc)
60 {
61     if (argc != 2)
62     {
63         command_puts(s, "Usage: search word\n");
64         return 1;
65     }
66     search(s->psession, argv[1]);
67     return 1;
68 }
69
70 static int cmd_hitsbytarget(struct command_session *s, char **argv, int argc)
71 {
72     int count;
73     int i;
74
75     struct hitsbytarget *ht = hitsbytarget(s->psession, &count);
76     for (i = 0; i < count; i++)
77     {
78         char buf[1024];
79
80         sprintf(buf, "%s: %d (%d records, diag=%d, state=%s conn=%d)\n", ht[i].id, ht[i].hits,
81             ht[i].records, ht[i].diagnostic, ht[i].state, ht[i].connected);
82         command_puts(s, buf);
83     }
84     return 1;
85 }
86
87 static int cmd_show(struct command_session *s, char **argv, int argc)
88 {
89     struct record **recs;
90     int num = 10;
91     int merged, total;
92     int i;
93
94     if (argc == 2)
95         num = atoi(argv[1]);
96
97     recs = show(s->psession, 0, &num, &merged, &total);
98
99     for (i = 0; i < num; i++)
100     {
101         int rc;
102         struct record *cnode;
103         struct record *r = recs[i];
104
105         command_puts(s, r->merge_key);
106         for (rc = 1, cnode = r->next_cluster; cnode; cnode = cnode->next_cluster, rc++)
107             ;
108         if (rc > 1)
109         {
110             char buf[256];
111             sprintf(buf, " (%d records)", rc);
112             command_puts(s, buf);
113         }
114         command_puts(s, "\n");
115     }
116     return 1;
117 }
118
119 static int cmd_stat(struct command_session *s, char **argv, int argc)
120 {
121     char buf[1024];
122     struct statistics stat;
123
124     statistics(s->psession, &stat);
125     sprintf(buf, "Number of connections: %d\n", stat.num_clients);
126     command_puts(s, buf);
127     if (stat.num_no_connection)
128     {
129         sprintf(buf, "#No_connection:        %d\n", stat.num_no_connection);
130         command_puts(s, buf);
131     }
132     if (stat.num_connecting)
133     {
134         sprintf(buf, "#Connecting:           %d\n", stat.num_connecting);
135         command_puts(s, buf);
136     }
137     if (stat.num_initializing)
138     {
139         sprintf(buf, "#Initializing:         %d\n", stat.num_initializing);
140         command_puts(s, buf);
141     }
142     if (stat.num_searching)
143     {
144         sprintf(buf, "#Searching:            %d\n", stat.num_searching);
145         command_puts(s, buf);
146     }
147     if (stat.num_presenting)
148     {
149         sprintf(buf, "#Ppresenting:          %d\n", stat.num_presenting);
150         command_puts(s, buf);
151     }
152     if (stat.num_idle)
153     {
154         sprintf(buf, "#Idle:                 %d\n", stat.num_idle);
155         command_puts(s, buf);
156     }
157     if (stat.num_failed)
158     {
159         sprintf(buf, "#Failed:               %d\n", stat.num_failed);
160         command_puts(s, buf);
161     }
162     if (stat.num_error)
163     {
164         sprintf(buf, "#Error:                %d\n", stat.num_error);
165         command_puts(s, buf);
166     }
167     return 1;
168 }
169
170 static struct {
171     char *cmd;
172     int (*fun)(struct command_session *s, char *argv[], int argc);
173 } cmd_array[] = {
174     {"quit", cmd_quit},
175 #ifdef GAGA
176     {"load", cmd_load},
177 #endif
178     {"find", cmd_search},
179     {"ht", cmd_hitsbytarget},
180     {"stat", cmd_stat},
181     {"show", cmd_show},
182     {0,0}
183 };
184
185 void command_command(struct command_session *s, char *command)
186 {
187     char *p;
188     char *argv[20];
189     int argc = 0;
190     int i;
191     int res = -1;
192
193     p = command;
194     while (*p)
195     {
196         while (isspace(*p))
197             p++;
198         if (!*p)
199             break;
200         argv[argc++] = p;
201         while (*p && !isspace(*p))
202             p++;
203         if (!*p)
204             break;
205         *(p++) = '\0';
206     }
207     if (argc) {
208         for (i = 0; cmd_array[i].cmd; i++)
209         {
210             if (!strcmp(cmd_array[i].cmd, argv[0])) {
211                 res = (cmd_array[i].fun)(s, argv, argc);
212
213                 break;
214             }
215         }
216         if (res < 0) {
217             command_puts(s, "Unknown command.\n");
218             command_prompt(s);
219         }
220         else if (res == 1) {
221             command_prompt(s);
222         }
223     }
224     else
225         command_prompt(s);
226
227 }
228
229
230 static void command_io(IOCHAN i, int event)
231 {
232     int res;
233     char buf[1024];
234     struct command_session *s;
235
236     s = iochan_getdata(i);
237
238
239     switch (event)
240     {
241         case EVENT_INPUT:
242             res = read(iochan_getfd(i), buf, 1024);
243             if (res <= 0)
244             {
245                 yaz_log(YLOG_WARN|YLOG_ERRNO, "read command");
246                 close(iochan_getfd(i));
247                 iochan_destroy(i);
248                 command_destroy(s);
249                 return;
250             }
251             if (!index(buf, '\n')) {
252                 yaz_log(YLOG_WARN|YLOG_ERRNO, "Did not receive complete command");
253                 close(iochan_getfd(i));
254                 iochan_destroy(i);
255                 command_destroy(s);
256                 return;
257             }
258             buf[res] = '\0';
259             command_command(s, buf);
260             break;
261         case EVENT_OUTPUT:
262             if (!s->outbuflen || s->outbufwrit < 0)
263             {
264                 yaz_log(YLOG_WARN, "Called with outevent but no data");
265                 iochan_clearflag(i, EVENT_OUTPUT);
266             }
267             else
268             {
269                 res = write(iochan_getfd(i), s->outbuf + s->outbufwrit, s->outbuflen -
270                     s->outbufwrit);
271                 if (res < 0) {
272                     yaz_log(YLOG_WARN|YLOG_ERRNO, "write command");
273                     close(iochan_getfd(i));
274                     iochan_destroy(i);
275                     command_destroy(s);
276                 }
277                 else
278                 {
279                     s->outbufwrit += res;
280                     if (s->outbufwrit >= s->outbuflen)
281                     {
282                         s->outbuflen = s->outbufwrit = 0;
283                         iochan_clearflag(i, EVENT_OUTPUT);
284                     }
285                 }
286             }
287             break;
288         default:
289             yaz_log(YLOG_WARN, "Bad voodoo on socket");
290     }
291 }
292
293 void command_puts(struct command_session *s, const char *buf)
294 {
295     int len = strlen(buf);
296     memcpy(s->outbuf + s->outbuflen, buf, len);
297     s->outbuflen += len;
298     iochan_setflag(s->channel, EVENT_OUTPUT);
299 }
300
301 void command_prompt(struct command_session *s)
302 {
303     command_puts(s, "Pazpar2> ");
304 }
305
306
307 /* Accept a new command connection */
308 static void command_accept(IOCHAN i, int event)
309 {
310     struct sockaddr_in addr;
311     int fd = iochan_getfd(i);
312     socklen_t len;
313     int s;
314     IOCHAN c;
315     struct command_session *ses;
316     int flags;
317
318     len = sizeof addr;
319     if ((s = accept(fd, (struct sockaddr *) &addr, &len)) < 0)
320     {
321         yaz_log(YLOG_WARN|YLOG_ERRNO, "accept");
322         return;
323     }
324     if ((flags = fcntl(s, F_GETFL, 0)) < 0) 
325         yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl");
326     if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0)
327         yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl2");
328
329     yaz_log(YLOG_LOG, "New command connection");
330     c = iochan_create(s, command_io, EVENT_INPUT | EVENT_EXCEPT);
331
332     ses = xmalloc(sizeof(*ses));
333     ses->outbuf = xmalloc(50000);
334     ses->outbuflen = 0;
335     ses->outbufwrit = 0;
336     ses->channel = c;
337     ses->psession = new_session();
338     iochan_setdata(c, ses);
339
340     command_puts(ses, "Welcome to pazpar2\n\n");
341     command_prompt(ses);
342
343     c->next = channel_list;
344     channel_list = c;
345 }
346
347 void command_destroy(struct command_session *s) {
348     xfree(s->outbuf);
349     xfree(s);
350 }
351
352 /* Create a command-channel listener */
353 void command_init(int port)
354 {
355     IOCHAN c;
356     int l;
357     struct protoent *p;
358     struct sockaddr_in myaddr;
359     int one = 1;
360
361     yaz_log(YLOG_LOG, "Command port is %d", port);
362     if (!(p = getprotobyname("tcp"))) {
363         abort();
364     }
365     if ((l = socket(PF_INET, SOCK_STREAM, p->p_proto)) < 0)
366         yaz_log(YLOG_FATAL|YLOG_ERRNO, "socket");
367     if (setsockopt(l, SOL_SOCKET, SO_REUSEADDR, (char*)
368                     &one, sizeof(one)) < 0)
369         abort();
370
371     bzero(&myaddr, sizeof myaddr);
372     myaddr.sin_family = AF_INET;
373     myaddr.sin_addr.s_addr = INADDR_ANY;
374     myaddr.sin_port = htons(port);
375     if (bind(l, (struct sockaddr *) &myaddr, sizeof myaddr) < 0) 
376         yaz_log(YLOG_FATAL|YLOG_ERRNO, "bind");
377     if (listen(l, SOMAXCONN) < 0) 
378         yaz_log(YLOG_FATAL|YLOG_ERRNO, "listen");
379
380     c = iochan_create(l, command_accept, EVENT_INPUT | EVENT_EXCEPT);
381     //iochan_setdata(c, &l);
382     c->next = channel_list;
383     channel_list = c;
384 }
385
386 /*
387  * Local variables:
388  * c-basic-offset: 4
389  * indent-tabs-mode: nil
390  * End:
391  * vim: shiftwidth=4 tabstop=8 expandtab
392  */