SafeTimer: check the IPC struct before trying to send a kill to the server in the...
[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 static CtdlIPC *ipc = NULL;
53
54 /*
55  * Why both cleanup() and nq_cleanup() ?  Notice the alarm() call in
56  * cleanup() .  If for some reason sendcommand hangs waiting for the server
57  * to clean up, the alarm clock goes off and the program exits anyway.
58  * The cleanup() routine makes a check to ensure it's not reentering, in
59  * case the ipc module looped it somehow.
60  */
61 void nq_cleanup(int e)
62 {
63         if (e == SIGALRM)
64                 fprintf(stderr, "\nWatch dog time out.\n");
65         exit(e);
66 }
67
68 /*
69  * send binary to server
70  */
71 void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
72 {
73         unsigned int bytes_written = 0;
74         int retval;
75 /*
76 #if defined(HAVE_OPENSSL)
77         if (ipc->ssl) {
78                 serv_write_ssl(ipc, buf, nbytes);
79                 return;
80         }
81 #endif
82 */
83         while (bytes_written < nbytes) {
84                 retval = write(ipc->sock, &buf[bytes_written],
85                                nbytes - bytes_written);
86                 if (retval < 1) {
87                         connection_died(ipc, 0);
88                         return;
89                 }
90                 bytes_written += retval;
91         }
92 }
93
94
95 void cleanup(int e)
96 {
97         static int nested = 0;
98
99         alarm(30);
100         signal(SIGALRM, nq_cleanup);
101         if ((ipc != NULL) && 
102             (ipc->sock > 0))
103                 serv_write(ipc, "\n", 1);
104         if ((nested++ < 1) &&
105             (ipc != NULL) &&
106             (ipc->sock > 0))
107                 CtdlIPCQuit(ipc);
108         nq_cleanup(e);
109 }
110
111 /*
112  * This is implemented as a function rather than as a macro because the
113  * client-side IPC modules expect logoff() to be defined.  They call logoff()
114  * when a problem connecting or staying connected to the server occurs.
115  */
116 void logoff(int e)
117 {
118         cleanup(e);
119 }
120
121 /*
122  * Connect sendcommand to the Citadel server running on this computer.
123  */
124 void np_attach_to_server(char *host, char *port)
125 {
126         char buf[SIZ];
127         char hostbuf[256], portbuf[256];
128         char *args[] =
129         {"sendcommand", NULL};
130         int r;
131
132         fprintf(stderr, "Attaching to server...\n");
133         strcpy(hostbuf, host);
134         strcpy(portbuf, port);
135         ipc = CtdlIPC_new(1, args, hostbuf, portbuf);
136         if (!ipc) {
137                 fprintf(stderr, "Can't connect: %s\n", strerror(errno));
138                 exit(3);
139         }
140         CtdlIPC_chat_recv(ipc, buf);
141         fprintf(stderr, "%s\n", &buf[4]);
142         snprintf(buf, sizeof buf, "IPGM %d", config.c_ipgm_secret);
143         r = CtdlIPCInternalProgram(ipc, config.c_ipgm_secret, buf);
144         fprintf(stderr, "%s\n", buf);
145         if (r / 100 != 2) {
146                 cleanup(2);
147         }
148 }
149
150
151 void sendcommand_die(void) {
152         exit(0);
153 }
154
155
156
157 int main(int argc, char **argv)
158 {
159         int a;
160         char cmd[SIZ];
161         char buf[SIZ];
162         int watchdog = 60;
163
164         int relh=0;
165         int home=0;
166         char relhome[PATH_MAX]="";
167         char ctdldir[PATH_MAX]=CTDLDIR;
168         fd_set read_fd;
169         struct timeval tv;
170         int ret;
171         int server_shutting_down = 0;
172         
173         strcpy(ctdl_home_directory, DEFAULT_PORT);
174
175         strcpy(cmd, "");
176         /*
177          * Change directories if specified
178          */
179         for (a = 1; a < argc; ++a) {
180                 if (!strncmp(argv[a], "-h", 2)) {
181                         relh=argv[a][2]!='/';
182                         if (!relh) safestrncpy(ctdl_home_directory, &argv[a][2],
183                                                                    sizeof ctdl_home_directory);
184                         else
185                                 safestrncpy(relhome, &argv[a][2],
186                                                         sizeof relhome);
187                         home=1;
188                 } else if (!strncmp(argv[a], "-w", 2)) {
189                         watchdog = atoi(&argv[a][2]);
190                         if (watchdog<1)
191                                 watchdog=1;
192                 } else {
193                         if (!IsEmptyStr(cmd))
194                                 strcat(cmd, " ");
195                         strcat(cmd, argv[a]);
196                 }
197         }
198
199         calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
200         get_config();
201
202         signal(SIGINT, cleanup);
203         signal(SIGQUIT, cleanup);
204         signal(SIGHUP, cleanup);
205         signal(SIGTERM, cleanup);
206
207         fprintf(stderr, "sendcommand: started (pid=%d) "
208                         "running in %s\n",
209                         (int) getpid(),
210                         ctdl_home_directory);
211         fflush(stderr);
212
213         alarm(watchdog);
214         signal(SIGALRM, nq_cleanup); /* Set up a watchdog type timer in case we hang */
215         
216         np_attach_to_server(UDS, ctdl_home_directory);
217         fflush(stderr);
218         setIPCDeathHook(sendcommand_die);
219
220         fprintf(stderr, "%s\n", cmd);
221         CtdlIPC_chat_send(ipc, cmd);
222         CtdlIPC_chat_recv(ipc, buf);
223         fprintf(stderr, "%s\n", buf);
224
225         tv.tv_sec = 0;
226         tv.tv_usec = 1000;
227
228         if (!strncasecmp(&buf[1], "31", 2)) {
229                 server_shutting_down = 1;
230         }
231
232         if (buf[0] == '1') {
233                 while (CtdlIPC_chat_recv(ipc, buf), strcmp(buf, "000")) {
234                         printf("%s\n", buf);
235                         alarm(watchdog); /* Kick the watchdog timer */
236                 }
237         } else if (buf[0] == '4') {
238                 do {
239                         if (fgets(buf, sizeof buf, stdin) == NULL)
240                                 strcpy(buf, "000");
241                         if (!IsEmptyStr(buf))
242                                 if (buf[strlen(buf) - 1] == '\n')
243                                         buf[strlen(buf) - 1] = 0;
244                         if (!IsEmptyStr(buf))
245                                 if (buf[strlen(buf) - 1] == '\r')
246                                         buf[strlen(buf) - 1] = 0;
247                         if (strcmp(buf, "000"))
248                                 CtdlIPC_chat_send(ipc, buf);
249                         
250                         FD_ZERO(&read_fd);
251                         FD_SET(ipc->sock, &read_fd);
252                         ret = select(ipc->sock+1, &read_fd, NULL, NULL,  &tv);
253                         if (ret == -1) {
254                                 if (!(errno == EINTR || errno == EAGAIN))
255                                         fprintf(stderr, "select() failed: %s", strerror(errno));
256                                 return(1);
257                         }
258
259                         if (ret != 0) {
260                                 size_t n;
261                                 char rbuf[SIZ];
262
263                                 rbuf[0] = '\0';
264                                 n = read(ipc->sock, rbuf, SIZ);
265                                 if (n>0) {
266                                         rbuf[n]='\0';
267                                         fprintf(stderr, "%s", rbuf);
268                                         fflush (stdout);
269                                 }
270                         }
271                         alarm(watchdog); /* Kick the watchdog timer */
272                 } while (strcmp(buf, "000"));
273                 CtdlIPC_chat_send(ipc, "\n");
274                 CtdlIPC_chat_send(ipc, "000");
275         }
276         alarm(0);       /* Shutdown the watchdog timer */
277         fprintf(stderr, "sendcommand: processing ended.\n");
278
279         /* Clean up and log off ... unless the server indicated that the command
280          * we sent is shutting it down, in which case we want to just cut the
281          * connection and exit.
282          */
283         if (server_shutting_down) {
284                 nq_cleanup(0);
285         }
286         else {
287                 cleanup(0);
288         }
289         return 0;
290 }
291
292 /*
293  * Stub function
294  */
295 void stty_ctdl(int cmd) {
296 }
297
298