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