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