1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * rmvolmgr daemon 30 */ 31 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <sys/types.h> 35 #include <sys/stat.h> 36 #include <dirent.h> 37 #include <signal.h> 38 #include <unistd.h> 39 #include <fcntl.h> 40 #include <strings.h> 41 #include <errno.h> 42 #include <libintl.h> 43 #include <sys/syscall.h> 44 #include <libscf.h> 45 #include <priv_utils.h> 46 47 #include <dbus/dbus.h> 48 #include <dbus/dbus-glib.h> 49 #include <dbus/dbus-glib-lowlevel.h> 50 #include <libhal.h> 51 52 #include "rmm_common.h" 53 54 char *progname = "rmvolmgr"; 55 56 #define RMVOLMGR_FMRI "svc:/system/filesystem/rmvolmgr:default" 57 58 typedef struct managed_volume { 59 char *udi; 60 boolean_t my; 61 struct action_arg aa; 62 } managed_volume_t; 63 64 static GSList *managed_volumes; 65 66 static GMainLoop *mainloop; 67 static LibHalContext *hal_ctx; 68 static int sigexit_pipe[2]; 69 static GIOChannel *sigexit_ioch; 70 71 static boolean_t opt_c; /* disable CDE compatibility */ 72 static boolean_t opt_n; /* disable legacy mountpoint symlinks */ 73 static boolean_t opt_s; /* system instance */ 74 75 static void get_smf_properties(); 76 static int daemon(int nochdir, int noclose); 77 static void rmm_device_added(LibHalContext *ctx, const char *udi); 78 static void rmm_device_removed(LibHalContext *ctx, const char *udi); 79 static void rmm_property_modified(LibHalContext *ctx, const char *udi, 80 const char *key, dbus_bool_t is_removed, dbus_bool_t is_added); 81 static void rmm_mount_all(); 82 static void rmm_unmount_all(); 83 static void sigexit(int signo); 84 static gboolean sigexit_ioch_func(GIOChannel *source, GIOCondition condition, 85 gpointer user_data); 86 87 static void 88 usage() 89 { 90 (void) fprintf(stderr, gettext("\nusage: rmvolmgr [-v]\n")); 91 } 92 93 static int 94 rmvolmgr(int argc, char **argv) 95 { 96 const char *opts = "chnsv"; 97 DBusError error; 98 boolean_t daemonize; 99 rmm_error_t rmm_error; 100 int c; 101 102 while ((c = getopt(argc, argv, opts)) != EOF) { 103 switch (c) { 104 case 'c': 105 opt_c = B_TRUE; 106 break; 107 case 'n': 108 opt_n = B_TRUE; 109 break; 110 case 's': 111 opt_s = B_TRUE; 112 break; 113 case 'v': 114 rmm_debug = 1; 115 break; 116 case '?': 117 case 'h': 118 usage(); 119 return (0); 120 default: 121 usage(); 122 return (1); 123 } 124 } 125 126 if (opt_s) { 127 if (geteuid() != 0) { 128 (void) fprintf(stderr, 129 gettext("system instance must have euid 0\n")); 130 return (1); 131 } 132 133 get_smf_properties(); 134 135 if (opt_c) { 136 rmm_vold_actions_enabled = B_FALSE; 137 } 138 if (opt_n) { 139 rmm_vold_mountpoints_enabled = B_FALSE; 140 } 141 142 143 /* 144 * Drop unused privileges. Remain root for HAL interaction 145 * and to create legacy symlinks. 146 * 147 * Need PRIV_FILE_DAC_WRITE to write to users' 148 * /tmp/.removable/notify* files. 149 */ 150 if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, 151 0, 0, 152 rmm_vold_actions_enabled ? PRIV_FILE_DAC_WRITE : NULL, 153 NULL) == -1) { 154 (void) fprintf(stderr, 155 gettext("failed to drop privileges")); 156 return (1); 157 } 158 /* basic privileges we don't need */ 159 (void) priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_PROC_EXEC, 160 PRIV_PROC_INFO, PRIV_FILE_LINK_ANY, PRIV_PROC_SESSION, 161 NULL); 162 163 } else { 164 if (opt_c) { 165 rmm_vold_actions_enabled = B_FALSE; 166 } 167 if (opt_n) { 168 rmm_vold_mountpoints_enabled = B_FALSE; 169 } 170 } 171 172 daemonize = (getenv("RMVOLMGR_NODAEMON") == NULL); 173 174 if (daemonize && daemon(0, 0) < 0) { 175 dprintf("daemonizing failed: %s", strerror(errno)); 176 return (1); 177 } 178 179 if (opt_s) { 180 __fini_daemon_priv(PRIV_PROC_FORK, NULL); 181 } 182 183 /* 184 * signal mainloop integration using pipes 185 */ 186 if (pipe(sigexit_pipe) != 0) { 187 dprintf("pipe failed %s\n", strerror(errno)); 188 return (1); 189 } 190 sigexit_ioch = g_io_channel_unix_new(sigexit_pipe[0]); 191 if (sigexit_ioch == NULL) { 192 dprintf("g_io_channel_unix_new failed\n"); 193 return (1); 194 } 195 g_io_add_watch(sigexit_ioch, G_IO_IN, sigexit_ioch_func, NULL); 196 signal(SIGTERM, sigexit); 197 signal(SIGINT, sigexit); 198 signal(SIGHUP, SIG_IGN); 199 signal(SIGUSR1, SIG_IGN); 200 signal(SIGUSR2, SIG_IGN); 201 202 if ((hal_ctx = rmm_hal_init(rmm_device_added, rmm_device_removed, 203 rmm_property_modified, &error, &rmm_error)) == NULL) { 204 dbus_error_free(&error); 205 return (1); 206 } 207 208 /* user instance should claim devices */ 209 if (!opt_s) { 210 if (!rmm_hal_claim_branch(hal_ctx, HAL_BRANCH_LOCAL)) { 211 (void) fprintf(stderr, 212 gettext("cannot claim branch\n")); 213 return (1); 214 } 215 } 216 217 rmm_mount_all(); 218 219 if ((mainloop = g_main_loop_new(NULL, B_FALSE)) == NULL) { 220 dprintf("Cannot create main loop\n"); 221 return (1); 222 } 223 224 g_main_loop_run(mainloop); 225 226 return (0); 227 } 228 229 static void 230 get_smf_properties() 231 { 232 scf_simple_prop_t *prop; 233 uint8_t *val; 234 235 if ((prop = scf_simple_prop_get(NULL, RMVOLMGR_FMRI, 236 "rmvolmgr", "legacy_mountpoints")) != NULL) { 237 if ((val = scf_simple_prop_next_boolean(prop)) != NULL) { 238 rmm_vold_mountpoints_enabled = (*val != 0); 239 } 240 scf_simple_prop_free(prop); 241 } 242 243 if ((prop = scf_simple_prop_get(NULL, RMVOLMGR_FMRI, 244 "rmvolmgr", "cde_compatible")) != NULL) { 245 if ((val = scf_simple_prop_next_boolean(prop)) != NULL) { 246 rmm_vold_actions_enabled = (*val != 0); 247 } 248 scf_simple_prop_free(prop); 249 } 250 } 251 252 /* ARGSUSED */ 253 static void 254 sigexit(int signo) 255 { 256 dprintf("signal to exit %d\n", signo); 257 258 write(sigexit_pipe[1], "s", 1); 259 } 260 261 /* ARGSUSED */ 262 static gboolean 263 sigexit_ioch_func(GIOChannel *source, GIOCondition condition, 264 gpointer user_data) 265 { 266 gchar buf[1]; 267 gsize bytes_read; 268 GError *error = NULL; 269 270 if (g_io_channel_read_chars(source, buf, 1, &bytes_read, &error) != 271 G_IO_STATUS_NORMAL) { 272 dprintf("g_io_channel_read_chars failed %s", error->message); 273 g_error_free(error); 274 return (TRUE); 275 } 276 277 dprintf("signal to exit\n"); 278 279 rmm_unmount_all(); 280 281 g_main_loop_quit(mainloop); 282 283 return (TRUE); 284 } 285 286 static managed_volume_t * 287 rmm_managed_alloc(LibHalContext *ctx, const char *udi) 288 { 289 managed_volume_t *v; 290 291 if ((v = calloc(1, sizeof (managed_volume_t))) == NULL) { 292 return (NULL); 293 } 294 if ((v->udi = strdup(udi)) == NULL) { 295 free(v); 296 return (NULL); 297 } 298 if (!rmm_volume_aa_from_prop(ctx, udi, NULL, &v->aa)) { 299 free(v->udi); 300 free(v); 301 return (NULL); 302 } 303 304 return (v); 305 } 306 307 static void 308 rmm_managed_free(managed_volume_t *v) 309 { 310 rmm_volume_aa_free(&v->aa); 311 free(v->udi); 312 free(v); 313 } 314 315 static gint 316 rmm_managed_compare_udi(gconstpointer a, gconstpointer b) 317 { 318 const managed_volume_t *va = a; 319 const char *udi = b; 320 321 return (strcmp(va->udi, udi)); 322 } 323 324 static boolean_t 325 volume_should_mount(const char *udi) 326 { 327 char *storage_device = NULL; 328 int ret = B_FALSE; 329 330 if (libhal_device_get_property_bool(hal_ctx, udi, 331 "volume.ignore", NULL)) { 332 goto out; 333 } 334 335 /* get the backing storage device */ 336 if (!(storage_device = libhal_device_get_property_string(hal_ctx, udi, 337 "block.storage_device", NULL))) { 338 dprintf("cannot get block.storage_device\n"); 339 goto out; 340 } 341 342 /* we handle either removable or hotpluggable */ 343 if (!libhal_device_get_property_bool(hal_ctx, storage_device, 344 "storage.removable", NULL) && 345 !libhal_device_get_property_bool(hal_ctx, storage_device, 346 "storage.hotpluggable", NULL)) { 347 goto out; 348 } 349 350 /* ignore if claimed by another volume manager */ 351 if (libhal_device_get_property_bool(hal_ctx, storage_device, 352 "info.claimed", NULL)) { 353 goto out; 354 } 355 356 ret = B_TRUE; 357 358 out: 359 libhal_free_string(storage_device); 360 return (ret); 361 } 362 363 static void 364 volume_added(const char *udi) 365 { 366 GSList *l; 367 managed_volume_t *v; 368 369 dprintf("volume added %s\n", udi); 370 371 l = g_slist_find_custom(managed_volumes, udi, rmm_managed_compare_udi); 372 v = (l != NULL) ? l->data : NULL; 373 374 if (v != NULL) { 375 dprintf("already managed %s\n", udi); 376 return; 377 } 378 if (!volume_should_mount(udi)) { 379 dprintf("should not mount %s\n", udi); 380 return; 381 } 382 if ((v = rmm_managed_alloc(hal_ctx, udi)) == NULL) { 383 return; 384 } 385 if (rmm_action(hal_ctx, udi, INSERT, &v->aa, 0, 0, 0)) { 386 v->my = B_TRUE; 387 managed_volumes = g_slist_prepend(managed_volumes, v); 388 } else { 389 dprintf("rmm_action failed %s\n", udi); 390 rmm_managed_free(v); 391 } 392 } 393 394 static void 395 volume_removed(const char *udi) 396 { 397 GSList *l; 398 managed_volume_t *v; 399 400 dprintf("volume removed %s\n", udi); 401 402 l = g_slist_find_custom(managed_volumes, udi, rmm_managed_compare_udi); 403 v = (l != NULL) ? l->data : NULL; 404 if (v == NULL) { 405 return; 406 } 407 408 /* HAL will unmount, just do the vold legacy stuff */ 409 v->aa.aa_action = EJECT; 410 (void) vold_postprocess(hal_ctx, udi, &v->aa); 411 412 rmm_managed_free(v); 413 managed_volumes = g_slist_delete_link(managed_volumes, l); 414 } 415 416 /* ARGSUSED */ 417 static void 418 rmm_device_added(LibHalContext *ctx, const char *udi) 419 { 420 if (libhal_device_query_capability(hal_ctx, udi, "volume", NULL)) { 421 volume_added(udi); 422 } 423 } 424 425 /* ARGSUSED */ 426 static void 427 rmm_device_removed(LibHalContext *ctx, const char *udi) 428 { 429 if (libhal_device_query_capability(hal_ctx, udi, "volume", NULL)) { 430 volume_removed(udi); 431 } 432 } 433 434 /* ARGSUSED */ 435 static void 436 rmm_property_modified(LibHalContext *ctx, const char *udi, const char *key, 437 dbus_bool_t is_removed, dbus_bool_t is_added) 438 { 439 DBusError error; 440 GSList *l; 441 managed_volume_t *v; 442 boolean_t is_mounted; 443 444 if (strcmp(key, "volume.is_mounted") != 0) { 445 return; 446 } 447 is_mounted = libhal_device_get_property_bool(hal_ctx, udi, key, NULL); 448 449 l = g_slist_find_custom(managed_volumes, udi, rmm_managed_compare_udi); 450 v = (l != NULL) ? l->data : NULL; 451 452 if (is_mounted) { 453 dprintf("Mounted: %s\n", udi); 454 455 if (v != NULL) { 456 /* volume mounted by us is already taken care of */ 457 if (v->my) { 458 return; 459 } 460 } else { 461 if ((v = rmm_managed_alloc(ctx, udi)) == NULL) { 462 return; 463 } 464 managed_volumes = g_slist_prepend(managed_volumes, v); 465 } 466 467 v->aa.aa_action = INSERT; 468 (void) vold_postprocess(hal_ctx, udi, &v->aa); 469 470 } else { 471 dprintf("Unmounted: %s\n", udi); 472 473 if (v == NULL) { 474 return; 475 } 476 477 v->aa.aa_action = EJECT; 478 (void) vold_postprocess(hal_ctx, udi, &v->aa); 479 480 rmm_managed_free(v); 481 managed_volumes = g_slist_delete_link(managed_volumes, l); 482 } 483 } 484 485 /* 486 * Mount all mountable volumes 487 */ 488 static void 489 rmm_mount_all() 490 { 491 DBusError error; 492 char **udis = NULL; 493 int num_udis; 494 int i; 495 managed_volume_t *v; 496 497 dbus_error_init(&error); 498 499 /* get all volumes */ 500 if ((udis = libhal_find_device_by_capability(hal_ctx, "volume", 501 &num_udis, &error)) == NULL) { 502 dprintf("mount_all: no volumes found\n"); 503 goto out; 504 } 505 506 for (i = 0; i < num_udis; i++) { 507 /* skip if already mounted */ 508 if (libhal_device_get_property_bool(hal_ctx, udis[i], 509 "volume.is_mounted", NULL)) { 510 dprintf("mount_all: %s already mounted\n", udis[i]); 511 continue; 512 } 513 if (!volume_should_mount(udis[i])) { 514 continue; 515 } 516 if ((v = rmm_managed_alloc(hal_ctx, udis[i])) == NULL) { 517 continue; 518 } 519 if (rmm_action(hal_ctx, udis[i], INSERT, &v->aa, 0, 0, 0)) { 520 v->my = B_TRUE; 521 managed_volumes = g_slist_prepend(managed_volumes, v); 522 } else { 523 rmm_managed_free(v); 524 } 525 } 526 527 out: 528 if (udis != NULL) { 529 libhal_free_string_array(udis); 530 } 531 rmm_dbus_error_free(&error); 532 } 533 534 /* 535 * Mount all volumes mounted by this program 536 */ 537 static void 538 rmm_unmount_all() 539 { 540 GSList *i; 541 managed_volume_t *v; 542 543 for (i = managed_volumes; i != NULL; i = managed_volumes) { 544 v = (managed_volume_t *)i->data; 545 546 if (v->my && libhal_device_get_property_bool(hal_ctx, v->udi, 547 "volume.is_mounted", NULL)) { 548 (void) rmm_action(hal_ctx, v->udi, UNMOUNT, 549 &v->aa, 0, 0, 0); 550 } 551 552 managed_volumes = g_slist_remove(managed_volumes, v); 553 rmm_managed_free(v); 554 } 555 } 556 557 static int 558 daemon(int nochdir, int noclose) 559 { 560 int fd; 561 562 switch (fork()) { 563 case -1: 564 return (-1); 565 case 0: 566 break; 567 default: 568 exit(0); 569 } 570 571 if (setsid() == -1) 572 return (-1); 573 574 if (!nochdir) 575 (void) chdir("/"); 576 577 if (!noclose) { 578 struct stat64 st; 579 580 if (((fd = open("/dev/null", O_RDWR, 0)) != -1) && 581 (fstat64(fd, &st) == 0)) { 582 if (S_ISCHR(st.st_mode) != 0) { 583 (void) dup2(fd, STDIN_FILENO); 584 (void) dup2(fd, STDOUT_FILENO); 585 (void) dup2(fd, STDERR_FILENO); 586 if (fd > 2) 587 (void) close(fd); 588 } else { 589 (void) close(fd); 590 (void) __set_errno(ENODEV); 591 return (-1); 592 } 593 } else { 594 (void) close(fd); 595 return (-1); 596 } 597 } 598 return (0); 599 } 600 601 int 602 main(int argc, char **argv) 603 { 604 vold_init(argc, argv); 605 606 return (rmvolmgr(argc, argv)); 607 } 608