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