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 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 * 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 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 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 * 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 * 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 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 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