cpdup - Fix NULL pointer indirection on lost socket
[dragonfly.git] / sbin / svc / remote.c
blob845d4ed2f243701111869c3261225a1940864367
1 /*
2 * Copyright (c) 2014 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
35 * Handle remote listen/connect and parsing operations.
38 #include "svc.h"
40 typedef struct SvcConnect {
41 struct SvcConnect *next;
42 command_t *cmd;
43 char *label;
44 pthread_t td;
45 int active;
46 int fd;
47 int rc;
48 FILE *fpr;
49 FILE *fpw;
50 } connect_t;
52 static void *remote_connect_thread(void *arg);
53 static void *remote_listener_thread(void *arg);
54 static void *remote_accepted_thread(void *arg);
55 static void remote_issue(connect_t *conn, command_t *cmd);
56 static int decode_args(connect_t *conn, char ***avp,
57 const char *ptr, size_t len);
59 connect_t *CHead;
60 connect_t **CNextP = &CHead;
64 * Execute cmd on the running service by connecting to the service, passing-in
65 * the command, and processing results.
67 * Called only by master process
69 void
70 remote_execute(command_t *cmd, const char *label)
72 connect_t *conn = calloc(sizeof(*conn), 1);
74 conn->fd = -1;
75 conn->cmd = cmd;
76 conn->label = strdup(label);
77 conn->active = 1;
78 conn->next = *CNextP;
79 *CNextP = conn;
81 pthread_create(&conn->td, NULL, remote_connect_thread, conn);
85 * Threaded connect/execute
87 static
88 void *
89 remote_connect_thread(void *arg)
91 connect_t *conn;
92 command_t *cmd;
93 struct sockaddr_un sou;
94 size_t len;
95 char *ptr;
97 conn = arg;
98 cmd = conn->cmd;
100 bzero(&sou, sizeof(sou));
101 if ((conn->fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0) {
102 sou.sun_family = AF_UNIX;
103 snprintf(sou.sun_path, sizeof(sou.sun_path),
104 "%s/service.%s.sk", cmd->piddir, conn->label);
105 len = strlen(sou.sun_path);
106 len = offsetof(struct sockaddr_un, sun_path[len+1]);
108 if (connect(conn->fd, (void *)&sou, len) < 0) {
109 close(conn->fd);
110 conn->fd = -1;
113 if (conn->fd >= 0) {
115 * Issue command
117 conn->fpr = fdopen(conn->fd, "r");
118 conn->fpw = fdopen(dup(conn->fd), "w");
119 conn->fd = -1;
120 setvbuf(conn->fpr, NULL, _IOFBF, 0);
121 setvbuf(conn->fpw, NULL, _IOFBF, 0);
122 remote_issue(conn, cmd);
123 while ((ptr = fgetln(conn->fpr, &len)) != NULL) {
124 if (len == 2 && ptr[0] == '.' && ptr[1] == '\n')
125 break;
128 * de-escape ..
130 if (len == 3 && ptr[0] == '.' && ptr[1] == '.' &&
131 ptr[2] == '\n') {
132 ++ptr;
133 --len;
135 fwrite(ptr, 1, len, cmd->fp);
136 fflush(cmd->fp);
138 conn->rc = 0;
139 conn->active = 0;
140 fclose(conn->fpr);
141 fclose(conn->fpw);
142 conn->fpr = NULL;
143 conn->fpw = NULL;
144 } else {
146 * Connection failed
148 fprintf(cmd->fp,
149 "Unable to connect to service %s\n",
150 conn->label);
151 conn->rc = 1;
152 conn->active = 0;
153 if (cmd->force_remove_files) {
154 fprintf(cmd->fp,
155 "Removing pid and socket files for %s\n",
156 conn->label);
157 remove_pid_and_socket(cmd, conn->label);
160 return NULL;
164 * Called only by master process
166 * Collect status from all remote commands.
169 remote_wait(void)
171 connect_t *scan;
172 int rc = 0;
174 while ((scan = CHead) != NULL) {
175 if (pthread_join(scan->td, NULL) < 0)
176 continue;
177 assert(scan->active == 0);
178 rc += scan->rc;
179 CHead = scan->next;
181 if (scan->fpr) {
182 fclose(scan->fpr);
183 scan->fpr = NULL;
185 if (scan->fpw) {
186 fclose(scan->fpw);
187 scan->fpw = NULL;
189 if (scan->fd >= 0) {
190 close(scan->fd);
191 scan->fd = -1;
193 if (scan->label) {
194 free(scan->label);
195 scan->label = NULL;
197 scan->cmd = NULL;
198 free(scan);
200 return rc;
204 * Create the unix domain socket and pid file for the service
205 * and start a thread to accept and process connections.
207 * Return 0 on success, non-zero if the socket could not be created.
209 void
210 remote_listener(command_t *cmd, int lfd)
212 connect_t *conn;
215 * child, create our unix domain socket listener thread.
217 conn = calloc(sizeof(*conn), 1);
218 conn->fd = lfd;
219 conn->cmd = cmd;
220 conn->label = strdup(cmd->label);
221 conn->active = 1;
223 conn->next = *CNextP;
224 *CNextP = conn;
225 pthread_create(&conn->td, NULL, remote_listener_thread, conn);
228 static void *
229 remote_listener_thread(void *arg)
231 connect_t *lconn = arg;
232 connect_t *conn;
233 struct sockaddr_un sou;
234 socklen_t len;
236 conn = calloc(sizeof(*conn), 1);
237 for (;;) {
238 len = sizeof(sou);
239 conn->fd = accept(lconn->fd, (void *)&sou, &len);
240 conn->label = strdup(lconn->label);
241 if (conn->fd < 0) {
242 if (errno == EINTR)
243 continue;
244 break;
246 pthread_create(&conn->td, NULL, remote_accepted_thread, conn);
247 conn = calloc(sizeof(*conn), 1);
249 free(conn);
251 return NULL;
254 static void *
255 remote_accepted_thread(void *arg)
257 connect_t *conn = arg;
258 command_t cmd;
259 char *ptr;
260 size_t len;
261 int rc;
262 int ac;
263 char **av;
265 pthread_detach(conn->td);
266 conn->fpr = fdopen(conn->fd, "r");
267 conn->fpw = fdopen(dup(conn->fd), "w");
268 conn->fd = -1;
269 setvbuf(conn->fpr, NULL, _IOFBF, 0);
270 setvbuf(conn->fpw, NULL, _IOFBF, 0);
272 while ((ptr = fgetln(conn->fpr, &len)) != NULL) {
273 ac = decode_args(conn, &av, ptr, len);
274 rc = process_cmd(&cmd, conn->fpw, ac, av);
275 cmd.cmdline = 0; /* we are the remote */
276 cmd.commanded = 1; /* commanded action (vs automatic) */
277 sreplace(&cmd.label, conn->label);
278 if (rc == 0) {
279 pthread_mutex_lock(&serial_mtx);
280 rc = execute_cmd(&cmd);
281 pthread_mutex_unlock(&serial_mtx);
283 free_cmd(&cmd);
284 afree(&av);
285 fwrite(".\n", 2, 1, conn->fpw);
286 fflush(conn->fpw);
288 fclose(conn->fpr);
289 fclose(conn->fpw);
290 conn->fpr = NULL;
291 conn->fpw = NULL;
292 free(conn->label);
293 free(conn);
295 return NULL;
299 * Issue the command to the remote, encode the arguments.
301 static
302 void
303 remote_issue(connect_t *conn, command_t *cmd)
305 int i;
307 for (i = 1; i < cmd->orig_ac; ++i) {
308 const char *str = cmd->orig_av[i];
310 if (i != 1)
311 putc(' ', conn->fpw);
312 while (*str) {
313 if (*str == ' ' || *str == '\\' || *str == '\n')
314 putc('\\', conn->fpw);
315 putc(*str, conn->fpw);
316 ++str;
319 putc('\n', conn->fpw);
320 fflush(conn->fpw);
324 * Decode arguments
326 static int
327 decode_args(connect_t *conn __unused, char ***avp, const char *ptr, size_t len)
329 char **av;
330 char *arg;
331 size_t i;
332 size_t j;
333 int acmax;
334 int ac;
336 if (len && ptr[len-1] == '\n')
337 --len;
339 acmax = 3; /* av[0], first arg, terminating NULL */
340 for (i = 0; i < len; ++i) {
341 if (ptr[i] == ' ')
342 ++acmax;
344 av = calloc(sizeof(char *), acmax);
345 av[0] = NULL;
346 ac = 1;
348 i = 0;
349 while (i < len) {
350 for (j = i; j < len; ++j) {
351 if (ptr[j] == ' ')
352 break;
354 arg = malloc(j - i + 1); /* worst case arg size */
355 j = 0;
356 while (i < len) {
357 if (ptr[i] == ' ')
358 break;
359 if (ptr[i] == '\\' && i + 1 < len) {
360 arg[j++] = ptr[i+1];
361 i += 2;
362 } else {
363 arg[j++] = ptr[i];
364 i += 1;
367 arg[j] = 0;
368 av[ac++] = arg;
369 if (i < len && ptr[i] == ' ')
370 ++i;
372 av[ac] = NULL;
374 #if 0
375 fprintf(conn->fpw, "DECODE ARGS: ");
376 for (i = 1; i < (size_t)ac; ++i)
377 fprintf(conn->fpw, " \"%s\"", av[i]);
378 fprintf(conn->fpw, "\n");
379 fflush(conn->fpw);
380 #endif
382 *avp = av;
383 return ac;