fix source so that '-Wformat -Werror=format-security' doesn't stop us from compiling
[citadel.git] / citadel / utils / sendcommand.c
1 /*
2  * Command-line utility to transmit a server command.
3  *
4  * Copyright (c) 1987-2010 by the citadel.org team
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "ctdl_module.h"
22
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <sys/wait.h>
27 #include <string.h>
28 #include <fcntl.h>
29 #include <stdio.h>
30 #include <ctype.h>
31
32 #if TIME_WITH_SYS_TIME
33 # include <sys/time.h>
34 # include <time.h>
35 #else
36 # if HAVE_SYS_TIME_H
37 #  include <sys/time.h>
38 # else
39 #  include <time.h>
40 # endif
41 #endif
42
43 #include <signal.h>
44 #include <errno.h>
45 #include <limits.h>
46 #include <libcitadel.h>
47 #include "citadel.h"
48 #include "citadel_ipc.h"
49 #include "server.h"
50 #include "config.h"
51
52 #define LOCKFILE "/tmp/LCK.sendcommand"
53
54 static CtdlIPC *ipc = NULL;
55
56 /*
57  * make sure only one copy of sendcommand runs at a time, using lock files
58  */
59 int set_lockfile(void)
60 {
61         FILE *lfp;
62         int onppid;
63         int rv;
64
65         if ((lfp = fopen(LOCKFILE, "r")) != NULL) {
66                 rv = fscanf(lfp, "%d", &onppid);
67                 fclose(lfp);
68                 if (!kill(onppid, 0) || errno == EPERM)
69                         return 1;
70         }
71         lfp = fopen(LOCKFILE, "w");
72         fprintf(lfp, "%ld\n", (long) getpid());
73         fclose(lfp);
74         return (0);
75 }
76
77 void remove_lockfile(void)
78 {
79         unlink(LOCKFILE);
80 }
81
82 /*
83  * Why both cleanup() and nq_cleanup() ?  Notice the alarm() call in
84  * cleanup() .  If for some reason sendcommand hangs waiting for the server
85  * to clean up, the alarm clock goes off and the program exits anyway.
86  * The cleanup() routine makes a check to ensure it's not reentering, in
87  * case the ipc module looped it somehow.
88  */
89 void nq_cleanup(int e)
90 {
91         if (e == SIGALRM)
92                 fprintf(stderr, "\nWatch dog time out.\n");
93         remove_lockfile();
94         exit(e);
95 }
96
97 /*
98  * send binary to server
99  */
100 void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
101 {
102         unsigned int bytes_written = 0;
103         int retval;
104 /*
105 #if defined(HAVE_OPENSSL)
106         if (ipc->ssl) {
107                 serv_write_ssl(ipc, buf, nbytes);
108                 return;
109         }
110 #endif
111 */
112         while (bytes_written < nbytes) {
113                 retval = write(ipc->sock, &buf[bytes_written],
114                                nbytes - bytes_written);
115                 if (retval < 1) {
116                         connection_died(ipc, 0);
117                         return;
118                 }
119                 bytes_written += retval;
120         }
121 }
122
123
124 void cleanup(int e)
125 {
126         static int nested = 0;
127
128         alarm(30);
129         signal(SIGALRM, nq_cleanup);
130         serv_write(ipc, "\n", 1);
131         if (nested++ < 1)
132                 CtdlIPCQuit(ipc);
133         nq_cleanup(e);
134 }
135
136 /*
137  * This is implemented as a function rather than as a macro because the
138  * client-side IPC modules expect logoff() to be defined.  They call logoff()
139  * when a problem connecting or staying connected to the server occurs.
140  */
141 void logoff(int e)
142 {
143         cleanup(e);
144 }
145
146 /*
147  * Connect sendcommand to the Citadel server running on this computer.
148  */
149 void np_attach_to_server(char *host, char *port)
150 {
151         char buf[SIZ];
152         char hostbuf[256], portbuf[256];
153         char *args[] =
154         {"sendcommand", NULL};
155         int r;
156
157         fprintf(stderr, "Attaching to server...\n");
158         strcpy(hostbuf, host);
159         strcpy(portbuf, port);
160         ipc = CtdlIPC_new(1, args, hostbuf, portbuf);
161         if (!ipc) {
162                 fprintf(stderr, "Can't connect: %s\n", strerror(errno));
163                 exit(3);
164         }
165         CtdlIPC_chat_recv(ipc, buf);
166         fprintf(stderr, "%s\n", &buf[4]);
167         snprintf(buf, sizeof buf, "IPGM %d", config.c_ipgm_secret);
168         r = CtdlIPCInternalProgram(ipc, config.c_ipgm_secret, buf);
169         fprintf(stderr, "%s\n", buf);
170         if (r / 100 != 2) {
171                 cleanup(2);
172         }
173 }
174
175
176 void sendcommand_die(void) {
177         exit(0);
178 }
179
180
181
182 int main(int argc, char **argv)
183 {
184         int a;
185         char cmd[SIZ];
186         char buf[SIZ];
187         int watchdog = 60;
188
189         int relh=0;
190         int home=0;
191         char relhome[PATH_MAX]="";
192         char ctdldir[PATH_MAX]=CTDLDIR;
193         fd_set read_fd;
194         struct timeval tv;
195         int ret;
196         int server_shutting_down = 0;
197         
198         strcpy(ctdl_home_directory, DEFAULT_PORT);
199
200         strcpy(cmd, "");
201         /*
202          * Change directories if specified
203          */
204         for (a = 1; a < argc; ++a) {
205                 if (!strncmp(argv[a], "-h", 2)) {
206                         relh=argv[a][2]!='/';
207                         if (!relh) safestrncpy(ctdl_home_directory, &argv[a][2],
208                                                                    sizeof ctdl_home_directory);
209                         else
210                                 safestrncpy(relhome, &argv[a][2],
211                                                         sizeof relhome);
212                         home=1;
213                 } else if (!strncmp(argv[a], "-w", 2)) {
214                         watchdog = atoi(&argv[a][2]);
215                         if (watchdog<1)
216                                 watchdog=1;
217                 } else {
218                         if (!IsEmptyStr(cmd))
219                                 strcat(cmd, " ");
220                         strcat(cmd, argv[a]);
221                 }
222         }
223
224         calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
225         get_config();
226
227         signal(SIGINT, cleanup);
228         signal(SIGQUIT, cleanup);
229         signal(SIGHUP, cleanup);
230         signal(SIGTERM, cleanup);
231
232         fprintf(stderr, "sendcommand: started (pid=%d) "
233                         "running in %s\n",
234                         (int) getpid(),
235                         ctdl_home_directory);
236         fflush(stderr);
237
238         alarm(watchdog);
239         signal(SIGALRM, nq_cleanup); /* Set up a watchdog type timer in case we hang */
240         
241         np_attach_to_server(UDS, ctdl_home_directory);
242         fflush(stderr);
243         setIPCDeathHook(sendcommand_die);
244
245         fprintf(stderr, "%s\n", cmd);
246         CtdlIPC_chat_send(ipc, cmd);
247         CtdlIPC_chat_recv(ipc, buf);
248         fprintf(stderr, "%s\n", buf);
249
250         tv.tv_sec = 0;
251         tv.tv_usec = 1000;
252
253         if (!strncasecmp(&buf[1], "31", 2)) {
254                 server_shutting_down = 1;
255         }
256
257         if (buf[0] == '1') {
258                 while (CtdlIPC_chat_recv(ipc, buf), strcmp(buf, "000")) {
259                         printf("%s\n", buf);
260                         alarm(watchdog); /* Kick the watchdog timer */
261                 }
262         } else if (buf[0] == '4') {
263                 do {
264                         if (fgets(buf, sizeof buf, stdin) == NULL)
265                                 strcpy(buf, "000");
266                         if (!IsEmptyStr(buf))
267                                 if (buf[strlen(buf) - 1] == '\n')
268                                         buf[strlen(buf) - 1] = 0;
269                         if (!IsEmptyStr(buf))
270                                 if (buf[strlen(buf) - 1] == '\r')
271                                         buf[strlen(buf) - 1] = 0;
272                         if (strcmp(buf, "000"))
273                                 CtdlIPC_chat_send(ipc, buf);
274                         
275                         FD_ZERO(&read_fd);
276                         FD_SET(ipc->sock, &read_fd);
277                         ret = select(ipc->sock+1, &read_fd, NULL, NULL,  &tv);
278                         if (ret == -1) {
279                                 if (!(errno == EINTR || errno == EAGAIN))
280                                         fprintf(stderr, "select() failed: %s", strerror(errno));
281                                 return(1);
282                         }
283
284                         if (ret != 0) {
285                                 size_t n;
286                                 char rbuf[SIZ];
287
288                                 rbuf[0] = '\0';
289                                 n = read(ipc->sock, rbuf, SIZ);
290                                 if (n>0) {
291                                         rbuf[n]='\0';
292                                         fprintf(stderr, "%s", rbuf);
293                                         fflush (stdout);
294                                 }
295                         }
296                         alarm(watchdog); /* Kick the watchdog timer */
297                 } while (strcmp(buf, "000"));
298                 CtdlIPC_chat_send(ipc, "\n");
299                 CtdlIPC_chat_send(ipc, "000");
300         }
301         alarm(0);       /* Shutdown the watchdog timer */
302         fprintf(stderr, "sendcommand: processing ended.\n");
303
304         /* Clean up and log off ... unless the server indicated that the command
305          * we sent is shutting it down, in which case we want to just cut the
306          * connection and exit.
307          */
308         if (server_shutting_down) {
309                 nq_cleanup(0);
310         }
311         else {
312                 cleanup(0);
313         }
314         return 0;
315 }
316
317 /*
318  * Stub function
319  */
320 void stty_ctdl(int cmd) {
321 }
322
323