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
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
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
35 * Handle remote listen/connect and parsing operations.
40 typedef struct SvcConnect {
41 struct SvcConnect *next;
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);
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
70 remote_execute(command_t *cmd, const char *label)
72 connect_t *conn = calloc(sizeof(*conn), 1);
76 conn->label = strdup(label);
81 pthread_create(&conn->td, NULL, remote_connect_thread, conn);
85 * Threaded connect/execute
89 remote_connect_thread(void *arg)
93 struct sockaddr_un sou;
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) {
117 conn->fpr = fdopen(conn->fd, "r");
118 conn->fpw = fdopen(dup(conn->fd), "w");
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')
130 if (len == 3 && ptr[0] == '.' && ptr[1] == '.' &&
135 fwrite(ptr, 1, len, cmd->fp);
149 "Unable to connect to service %s\n",
153 if (cmd->force_remove_files) {
155 "Removing pid and socket files for %s\n",
157 remove_pid_and_socket(cmd, conn->label);
164 * Called only by master process
166 * Collect status from all remote commands.
174 while ((scan = CHead) != NULL) {
175 if (pthread_join(scan->td, NULL) < 0)
177 assert(scan->active == 0);
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.
210 remote_listener(command_t *cmd, int lfd)
215 * child, create our unix domain socket listener thread.
217 conn = calloc(sizeof(*conn), 1);
220 conn->label = strdup(cmd->label);
223 conn->next = *CNextP;
225 pthread_create(&conn->td, NULL, remote_listener_thread, conn);
229 remote_listener_thread(void *arg)
231 connect_t *lconn = arg;
233 struct sockaddr_un sou;
236 conn = calloc(sizeof(*conn), 1);
239 conn->fd = accept(lconn->fd, (void *)&sou, &len);
240 conn->label = strdup(lconn->label);
246 pthread_create(&conn->td, NULL, remote_accepted_thread, conn);
247 conn = calloc(sizeof(*conn), 1);
255 remote_accepted_thread(void *arg)
257 connect_t *conn = arg;
265 pthread_detach(conn->td);
266 conn->fpr = fdopen(conn->fd, "r");
267 conn->fpw = fdopen(dup(conn->fd), "w");
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);
279 pthread_mutex_lock(&serial_mtx);
280 rc = execute_cmd(&cmd);
281 pthread_mutex_unlock(&serial_mtx);
285 fwrite(".\n", 2, 1, conn->fpw);
299 * Issue the command to the remote, encode the arguments.
303 remote_issue(connect_t *conn, command_t *cmd)
307 for (i = 1; i < cmd->orig_ac; ++i) {
308 const char *str = cmd->orig_av[i];
311 putc(' ', conn->fpw);
313 if (*str == ' ' || *str == '\\' || *str == '\n')
314 putc('\\', conn->fpw);
315 putc(*str, conn->fpw);
319 putc('\n', conn->fpw);
327 decode_args(connect_t *conn __unused, char ***avp, const char *ptr, size_t len)
336 if (len && ptr[len-1] == '\n')
339 acmax = 3; /* av[0], first arg, terminating NULL */
340 for (i = 0; i < len; ++i) {
344 av = calloc(sizeof(char *), acmax);
350 for (j = i; j < len; ++j) {
354 arg = malloc(j - i + 1); /* worst case arg size */
359 if (ptr[i] == '\\' && i + 1 < len) {
369 if (i < len && ptr[i] == ' ')
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");