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