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