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");