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