SENDCOMMAND: use linebuffered readers with dynamic buffer allocation to overcome...
[citadel.git] / citadel / utils / sendcommand.c
1 /*
2  * Command-line utility to transmit a server command.
3  *
4  * Copyright (c) 1987-2012 by the citadel.org team
5  *
6  * This program is open source software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 3.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <sys/types.h>
19 #include <sys/wait.h>
20 #include <string.h>
21 #include <fcntl.h>
22 #include <stdio.h>
23 #include <ctype.h>
24 #include <signal.h>
25 #include <errno.h>
26 #include <limits.h>
27 #include <sys/socket.h>
28 #include <sys/un.h>
29 #include "citadel.h"
30 #include "include/citadel_dirs.h"
31 #include <libcitadel.h>
32
33
34 int serv_sock = (-1);
35
36
37 int uds_connectsock(char *sockpath)
38 {
39         int s;
40         struct sockaddr_un addr;
41
42         memset(&addr, 0, sizeof(addr));
43         addr.sun_family = AF_UNIX;
44         strncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
45
46         s = socket(AF_UNIX, SOCK_STREAM, 0);
47         if (s < 0) {
48                 fprintf(stderr, "sendcommand: Can't create socket: %s\n", strerror(errno));
49                 exit(3);
50         }
51
52         if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
53                 fprintf(stderr, "sendcommand: can't connect: %s\n", strerror(errno));
54                 close(s);
55                 exit(3);
56         }
57
58         return s;
59 }
60
61
62 /*
63  * input binary data from socket
64  */
65 void serv_read(char *buf, int bytes)
66 {
67         int len, rlen;
68
69         len = 0;
70         while (len < bytes) {
71                 rlen = read(serv_sock, &buf[len], bytes - len);
72                 if (rlen < 1) {
73                         return;
74                 }
75                 len = len + rlen;
76         }
77 }
78
79
80 /*
81  * send binary to server
82  */
83 void serv_write(char *buf, int nbytes)
84 {
85         int bytes_written = 0;
86         int retval;
87         while (bytes_written < nbytes) {
88                 retval = write(serv_sock, &buf[bytes_written], nbytes - bytes_written);
89                 if (retval < 1) {
90                         return;
91                 }
92                 bytes_written = bytes_written + retval;
93         }
94 }
95
96
97
98 /*
99  * input string from socket - implemented in terms of serv_read()
100  */
101 void serv_gets(char *buf)
102 {
103         int i;
104
105         /* Read one character at a time.
106          */
107         for (i = 0;; i++) {
108                 serv_read(&buf[i], 1);
109                 if (buf[i] == '\n' || i == (SIZ-1))
110                         break;
111         }
112
113         /* If we got a long line, discard characters until the newline.
114          */
115         if (i == (SIZ-1)) {
116                 while (buf[i] != '\n') {
117                         serv_read(&buf[i], 1);
118                 }
119         }
120
121         /* Strip all trailing nonprintables (crlf)
122          */
123         buf[i] = 0;
124 }
125
126
127 /*
128  * send line to server - implemented in terms of serv_write()
129  */
130 void serv_puts(char *buf)
131 {
132         serv_write(buf, strlen(buf));
133         serv_write("\n", 1);
134 }
135
136
137
138
139 /*
140  * Main loop.  Do things and have fun.
141  */
142 int main(int argc, char **argv)
143 {
144         int a;
145         int watchdog = 60;
146         char buf[SIZ];
147         int xfermode = 0;
148         int relh=0;
149         int home=0;
150         char relhome[PATH_MAX]="";
151         char ctdldir[PATH_MAX]=CTDLDIR;
152
153         /* Parse command line */
154         while ((a = getopt(argc, argv, "h:w:")) != EOF) {
155                 switch (a) {
156                 case 'h':
157                         relh=optarg[0]!='/';
158                         if (!relh) {
159                                 strncpy(ctdl_home_directory, optarg, sizeof ctdl_home_directory);
160                         } else {
161                                 strncpy(relhome, optarg, sizeof relhome);
162                         }
163                         home = 1;
164                         break;
165                 case 'w':
166                         watchdog = atoi(optarg);
167                         break;
168                 default:
169                         fprintf(stderr, "sendcommand: usage: sendcommand [-h server_dir] [-w watchdog_timeout]\n");
170                         return(1);
171                 }
172         }
173
174         calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
175
176         fprintf(stderr, "sendcommand: started (pid=%d) connecting to Citadel server at %s\n",
177                 (int) getpid(),
178                 file_citadel_admin_socket
179         );
180         fflush(stderr);
181
182 //      alarm(watchdog);
183         serv_sock = uds_connectsock(file_citadel_admin_socket);
184
185         serv_gets(buf);
186         fprintf(stderr, "%s\n", buf);
187
188         strcpy(buf, "");
189         for (a=optind; a<argc; ++a) {
190                 if (a != optind) {
191                         strcat(buf, " ");
192                 }
193                 strcat(buf, argv[a]);
194         }
195
196         fprintf(stderr, "%s\n", buf);
197         serv_puts(buf);
198         serv_gets(buf);
199         fprintf(stderr, "%s\n", buf);
200
201         xfermode = buf[0];
202
203         if ((xfermode == '4') || (xfermode == '8')) {           /* send text */
204                 IOBuffer IOB;
205                 FDIOBuffer FDIO;
206                 const char *ErrStr;
207
208                 memset(&IOB, 0, sizeof(0));
209                 IOB.Buf = NewStrBufPlain(NULL, SIZ);
210                 IOB.fd = serv_sock;
211                 FDIOBufferInit(&FDIO, &IOB, fileno(stdin), -1);
212
213                 while (FileSendChunked(&FDIO, &ErrStr));
214                         alarm(watchdog);                        /* reset the watchdog timer */
215                 FDIOBufferDelete(&FDIO);
216                 FreeStrBuf(&IOB.Buf);
217                 serv_puts("000");
218         }
219
220         if ((xfermode == '1') || (xfermode == '8')) {           /* receive text */
221                 IOBuffer IOB;
222                 StrBuf *Line, *OutBuf;
223                 int Finished = 0;
224
225                 memset(&IOB, 0, sizeof(IOB));
226                 IOB.Buf = NewStrBufPlain(NULL, SIZ);
227                 IOB.fd = serv_sock;
228                 Line = NewStrBufPlain(NULL, SIZ);
229                 OutBuf = NewStrBufPlain(NULL, SIZ * 10);
230
231                 while (!Finished && (StrBuf_read_one_chunk_callback (serv_sock, 0, &IOB) >= 0))
232                 {
233                         eReadState State;
234
235                         State = eReadSuccess;
236                         while (!Finished && (State == eReadSuccess))
237                         {
238                                 if (IOBufferStrLength(&IOB) == 0)
239                                 {
240                                         State = eMustReadMore;
241                                         break;
242                                 }
243                                 State = StrBufChunkSipLine(Line, &IOB);
244                                 switch (State)
245                                 {
246                                 case eReadSuccess:
247                                         if (!strcmp(ChrPtr(Line), "000"))
248                                         {
249                                                 Finished = 1;
250                                                 break;
251                                         }
252                                         StrBufAppendBuf(OutBuf, Line, 0);
253                                         StrBufAppendBufPlain(OutBuf, HKEY("\n"), 0);
254 //                                      alarm(watchdog);                        /* reset the watchdog timer */
255                                         break;
256                                 case eBufferNotEmpty:
257                                         break;
258                                 case eMustReadMore:
259                                         continue;
260                                 case eReadFail:
261                                         fprintf(stderr, "WTF? Exit!\n");
262                                         exit(-1);
263                                         break;
264                                 }
265                                 if (StrLength(OutBuf) > 5*SIZ)
266                                 {
267                                         fwrite(ChrPtr(OutBuf), 1, StrLength(OutBuf), stdout);
268                                         FlushStrBuf(OutBuf);
269                                 }
270                         }
271                 }
272                 if (StrLength(OutBuf) > 0)
273                 {
274                         fwrite(ChrPtr(OutBuf), 1, StrLength(OutBuf), stdout);
275                 }
276                 FreeStrBuf(&Line);
277                 FreeStrBuf(&OutBuf);
278                 FreeStrBuf(&IOB.Buf);
279         }
280         
281         if (xfermode == '6') {                                  /* receive binary */
282                 size_t len = atoi(&buf[4]);
283                 size_t bytes_remaining = len;
284
285                 while (bytes_remaining > 0) {
286                         size_t this_block = bytes_remaining;
287                         if (this_block > SIZ) this_block = SIZ;
288                         serv_read(buf, this_block);
289                         fwrite(buf, this_block, 1, stdout);
290                         bytes_remaining -= this_block;
291                 }
292         }
293
294         close(serv_sock);
295         alarm(0);                                               /* cancel the watchdog timer */
296         fprintf(stderr, "sendcommand: processing ended.\n");
297         if (xfermode == '5') {
298                 return(1);
299         }
300         return(0);
301 }
302
303
304
305
306
307
308
309
310
311
312
313
314
315