xref: /dragonfly/sbin/svc/remote.c (revision 82f395276fa6ba7906ecc95917af85f30a54d7a1)
1 /*
2  * Copyright (c) 2014 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
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.
20  *
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.
33  */
34 /*
35  * Handle remote listen/connect and parsing operations.
36  */
37 
38 #include "svc.h"
39 
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;
51 
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);
58 
59 connect_t *CHead;
60 connect_t **CNextP = &CHead;
61 
62 
63 /*
64  * Execute cmd on the running service by connecting to the service, passing-in
65  * the command, and processing results.
66  *
67  * Called only by master process
68  */
69 void
remote_execute(command_t * cmd,const char * label)70 remote_execute(command_t *cmd, const char *label)
71 {
72           connect_t *conn = calloc(sizeof(*conn), 1);
73 
74           conn->fd = -1;
75           conn->cmd = cmd;
76           conn->label = strdup(label);
77           conn->active = 1;
78           conn->next = *CNextP;
79           *CNextP = conn;
80 
81           pthread_create(&conn->td, NULL, remote_connect_thread, conn);
82 }
83 
84 /*
85  * Threaded connect/execute
86  */
87 static
88 void *
remote_connect_thread(void * arg)89 remote_connect_thread(void *arg)
90 {
91           connect_t *conn;
92           command_t *cmd;
93           struct sockaddr_un sou;
94           size_t len;
95           char *ptr;
96 
97           conn = arg;
98           cmd = conn->cmd;
99 
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]);
107 
108                     if (connect(conn->fd, (void *)&sou, len) < 0) {
109                               close(conn->fd);
110                               conn->fd = -1;
111                     }
112           }
113           if (conn->fd >= 0) {
114                     /*
115                      * Issue command
116                      */
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;
126 
127                               /*
128                                * de-escape ..
129                                */
130                               if (len == 3 && ptr[0] == '.' && ptr[1] == '.' &&
131                                   ptr[2] == '\n') {
132                                         ++ptr;
133                                         --len;
134                               }
135                               fwrite(ptr, 1, len, cmd->fp);
136                               fflush(cmd->fp);
137                     }
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 {
145                     /*
146                      * Connection failed
147                      */
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);
158                     }
159           }
160           return NULL;
161 }
162 
163 /*
164  * Called only by master process
165  *
166  * Collect status from all remote commands.
167  */
168 int
remote_wait(void)169 remote_wait(void)
170 {
171           connect_t *scan;
172           int rc = 0;
173 
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;
180 
181                     if (scan->fpr) {
182                               fclose(scan->fpr);
183                               scan->fpr = NULL;
184                     }
185                     if (scan->fpw) {
186                               fclose(scan->fpw);
187                               scan->fpw = NULL;
188                     }
189                     if (scan->fd >= 0) {
190                               close(scan->fd);
191                               scan->fd = -1;
192                     }
193                     if (scan->label) {
194                               free(scan->label);
195                               scan->label = NULL;
196                     }
197                     scan->cmd = NULL;
198                     free(scan);
199           }
200           return rc;
201 }
202 
203 /*
204  * Create the unix domain socket and pid file for the service
205  * and start a thread to accept and process connections.
206  *
207  * Return 0 on success, non-zero if the socket could not be created.
208  */
209 void
remote_listener(command_t * cmd,int lfd)210 remote_listener(command_t *cmd, int lfd)
211 {
212           connect_t *conn;
213 
214           /*
215            * child, create our unix domain socket listener thread.
216            */
217           conn = calloc(sizeof(*conn), 1);
218           conn->fd = lfd;
219           conn->cmd = cmd;
220           conn->label = strdup(cmd->label);
221           conn->active = 1;
222 
223           conn->next = *CNextP;
224           *CNextP = conn;
225           pthread_create(&conn->td, NULL, remote_listener_thread, conn);
226 }
227 
228 static void *
remote_listener_thread(void * arg)229 remote_listener_thread(void *arg)
230 {
231           connect_t *lconn = arg;
232           connect_t *conn;
233           struct sockaddr_un sou;
234           socklen_t len;
235 
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;
245                     }
246                     pthread_create(&conn->td, NULL, remote_accepted_thread, conn);
247                     conn = calloc(sizeof(*conn), 1);
248           }
249           free(conn);
250 
251           return NULL;
252 }
253 
254 static void *
remote_accepted_thread(void * arg)255 remote_accepted_thread(void *arg)
256 {
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;
264 
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);
271 
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);
282                     }
283                     free_cmd(&cmd);
284                     afree(&av);
285                     fwrite(".\n", 2, 1, conn->fpw);
286                     fflush(conn->fpw);
287           }
288           fclose(conn->fpr);
289           fclose(conn->fpw);
290           conn->fpr = NULL;
291           conn->fpw = NULL;
292           free(conn->label);
293           free(conn);
294 
295           return NULL;
296 }
297 
298 /*
299  * Issue the command to the remote, encode the arguments.
300  */
301 static
302 void
remote_issue(connect_t * conn,command_t * cmd)303 remote_issue(connect_t *conn, command_t *cmd)
304 {
305           int i;
306 
307           for (i = 1; i < cmd->orig_ac; ++i) {
308                     const char *str = cmd->orig_av[i];
309 
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;
317                     }
318           }
319           putc('\n', conn->fpw);
320           fflush(conn->fpw);
321 }
322 
323 /*
324  * Decode arguments
325  */
326 static int
decode_args(connect_t * conn __unused,char *** avp,const char * ptr,size_t len)327 decode_args(connect_t *conn __unused, char ***avp, const char *ptr, size_t len)
328 {
329           char **av;
330           char *arg;
331           size_t i;
332           size_t j;
333           int acmax;
334           int ac;
335 
336           if (len && ptr[len-1] == '\n')
337                     --len;
338 
339           acmax = 3;          /* av[0], first arg, terminating NULL */
340           for (i = 0; i < len; ++i) {
341                     if (ptr[i] == ' ')
342                               ++acmax;
343           }
344           av = calloc(sizeof(char *), acmax);
345           av[0] = NULL;
346           ac = 1;
347 
348           i = 0;
349           while (i < len) {
350                     for (j = i; j < len; ++j) {
351                               if (ptr[j] == ' ')
352                                         break;
353                     }
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;
365                               }
366                     }
367                     arg[j] = 0;
368                     av[ac++] = arg;
369                     if (i < len && ptr[i] == ' ')
370                               ++i;
371           }
372           av[ac] = NULL;
373 
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
381 
382           *avp = av;
383           return ac;
384 }
385