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