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 #include <stdio.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <strings.h>
30 #include <stdarg.h>
31 #include <fcntl.h>
32 #include <libintl.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <ctype.h>
36 #include <sys/param.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/mnttab.h>
40
41 #include <dbus/dbus.h>
42 #include <dbus/dbus-glib.h>
43 #include <dbus/dbus-glib-lowlevel.h>
44 #include <libhal.h>
45 #include <libhal-storage.h>
46
47 #include "rmm_common.h"
48
49 #define RMM_PRINT_DEVICE_WIDTH 20
50
51 extern int rmm_debug;
52
53 static const char *action_strings[] = {
54 "eject",
55 "mount",
56 "remount",
57 "unmount",
58 "clear_mounts",
59 "closetray"
60 };
61
62
63 LibHalContext *
rmm_hal_init(LibHalDeviceAdded devadd_cb,LibHalDeviceRemoved devrem_cb,LibHalDevicePropertyModified propmod_cb,LibHalDeviceCondition cond_cb,DBusError * error,rmm_error_t * rmm_error)64 rmm_hal_init(LibHalDeviceAdded devadd_cb, LibHalDeviceRemoved devrem_cb,
65 LibHalDevicePropertyModified propmod_cb, LibHalDeviceCondition cond_cb,
66 DBusError *error, rmm_error_t *rmm_error)
67 {
68 DBusConnection *dbus_conn;
69 LibHalContext *ctx;
70 char **devices;
71 int nr;
72
73 dbus_error_init(error);
74
75 /*
76 * setup D-Bus connection
77 */
78 if (!(dbus_conn = dbus_bus_get(DBUS_BUS_SYSTEM, error))) {
79 dbgprintf("cannot get system bus: %s\n",
80 rmm_strerror(error, -1));
81 *rmm_error = RMM_EDBUS_CONNECT;
82 return (NULL);
83 }
84 rmm_dbus_error_free(error);
85
86 dbus_connection_setup_with_g_main(dbus_conn, NULL);
87 dbus_connection_set_exit_on_disconnect(dbus_conn, B_TRUE);
88
89 if ((ctx = libhal_ctx_new()) == NULL) {
90 dbgprintf("libhal_ctx_new failed");
91 *rmm_error = RMM_EHAL_CONNECT;
92 return (NULL);
93 }
94
95 libhal_ctx_set_dbus_connection(ctx, dbus_conn);
96
97 /*
98 * register callbacks
99 */
100 if (devadd_cb != NULL) {
101 libhal_ctx_set_device_added(ctx, devadd_cb);
102 }
103 if (devrem_cb != NULL) {
104 libhal_ctx_set_device_removed(ctx, devrem_cb);
105 }
106 if (propmod_cb != NULL) {
107 libhal_ctx_set_device_property_modified(ctx, propmod_cb);
108 if (!libhal_device_property_watch_all(ctx, error)) {
109 dbgprintf("property_watch_all failed %s",
110 rmm_strerror(error, -1));
111 libhal_ctx_free(ctx);
112 *rmm_error = RMM_EHAL_CONNECT;
113 return (NULL);
114 }
115 }
116 if (cond_cb != NULL) {
117 libhal_ctx_set_device_condition(ctx, cond_cb);
118 }
119
120 if (!libhal_ctx_init(ctx, error)) {
121 dbgprintf("libhal_ctx_init failed: %s",
122 rmm_strerror(error, -1));
123 libhal_ctx_free(ctx);
124 *rmm_error = RMM_EHAL_CONNECT;
125 return (NULL);
126 }
127 rmm_dbus_error_free(error);
128
129 /*
130 * The above functions do not guarantee that HAL is actually running.
131 * Check by invoking a method.
132 */
133 if (!(devices = libhal_get_all_devices(ctx, &nr, error))) {
134 dbgprintf("HAL is not running: %s", rmm_strerror(error, -1));
135 libhal_ctx_shutdown(ctx, NULL);
136 libhal_ctx_free(ctx);
137 *rmm_error = RMM_EHAL_CONNECT;
138 return (NULL);
139 } else {
140 rmm_dbus_error_free(error);
141 libhal_free_string_array(devices);
142 }
143
144 return (ctx);
145 }
146
147
148 void
rmm_hal_fini(LibHalContext * hal_ctx)149 rmm_hal_fini(LibHalContext *hal_ctx)
150 {
151 DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
152
153 (void) dbus_connection_unref(dbus_conn);
154 (void) libhal_ctx_free(hal_ctx);
155 }
156
157
158 /*
159 * find volume from any type of name, similar to the old media_findname()
160 * returns the LibHalDrive object and a list of LibHalVolume objects.
161 */
162 LibHalDrive *
rmm_hal_volume_find(LibHalContext * hal_ctx,const char * name,DBusError * error,GSList ** volumes)163 rmm_hal_volume_find(LibHalContext *hal_ctx, const char *name, DBusError *error,
164 GSList **volumes)
165 {
166 LibHalDrive *drive;
167 char *p;
168 char lastc;
169
170 *volumes = NULL;
171
172 /* temporarily remove trailing slash */
173 p = (char *)name + strlen(name) - 1;
174 if (*p == '/') {
175 lastc = *p;
176 *p = '\0';
177 } else {
178 p = NULL;
179 }
180
181 if (name[0] == '/') {
182 if (((drive = rmm_hal_volume_findby(hal_ctx,
183 "info.udi", name, volumes)) != NULL) ||
184 ((drive = rmm_hal_volume_findby(hal_ctx,
185 "block.device", name, volumes)) != NULL) ||
186 ((drive = rmm_hal_volume_findby(hal_ctx,
187 "block.solaris.raw_device", name, volumes)) != NULL) ||
188 ((drive = rmm_hal_volume_findby(hal_ctx,
189 "volume.mount_point", name, volumes)) != NULL)) {
190 goto out;
191 } else {
192 goto out;
193 }
194 }
195
196 /* try volume label */
197 if ((drive = rmm_hal_volume_findby(hal_ctx,
198 "volume.label", name, volumes)) != NULL) {
199 goto out;
200 }
201
202 drive = rmm_hal_volume_findby_nickname(hal_ctx, name, volumes);
203
204 out:
205 if (p != NULL) {
206 *p = lastc;
207 }
208 return (drive);
209 }
210
211 /*
212 * find default volume. Returns volume pointer and name in 'name'.
213 */
214 LibHalDrive *
rmm_hal_volume_find_default(LibHalContext * hal_ctx,DBusError * error,const char ** name_out,GSList ** volumes)215 rmm_hal_volume_find_default(LibHalContext *hal_ctx, DBusError *error,
216 const char **name_out, GSList **volumes)
217 {
218 LibHalDrive *drive;
219 static const char *names[] = { "floppy", "cdrom", "rmdisk" };
220 int i;
221
222 *volumes = NULL;
223
224 for (i = 0; i < NELEM(names); i++) {
225 if ((drive = rmm_hal_volume_findby_nickname(hal_ctx,
226 names[i], volumes)) != NULL) {
227 /*
228 * Skip floppy if it has no media.
229 * XXX might want to actually check for media
230 * every time instead of relying on volcheck.
231 */
232 if ((strcmp(names[i], "floppy") != 0) ||
233 libhal_device_get_property_bool(hal_ctx,
234 libhal_drive_get_udi(drive),
235 "storage.removable.media_available", NULL)) {
236 *name_out = names[i];
237 break;
238 }
239 }
240 rmm_dbus_error_free(error);
241 }
242
243 return (drive);
244 }
245
246 /*
247 * find volume by property=value
248 * returns the LibHalDrive object and a list of LibHalVolume objects.
249 * XXX add support for multiple properties, reduce D-Bus traffic
250 */
251 LibHalDrive *
rmm_hal_volume_findby(LibHalContext * hal_ctx,const char * property,const char * value,GSList ** volumes)252 rmm_hal_volume_findby(LibHalContext *hal_ctx, const char *property,
253 const char *value, GSList **volumes)
254 {
255 DBusError error;
256 LibHalDrive *drive = NULL;
257 LibHalVolume *v = NULL;
258 char **udis;
259 int num_udis;
260 int i;
261 int i_drive = -1;
262
263 *volumes = NULL;
264
265 dbus_error_init(&error);
266
267 /* get all devices with property=value */
268 if ((udis = libhal_manager_find_device_string_match(hal_ctx, property,
269 value, &num_udis, &error)) == NULL) {
270 rmm_dbus_error_free(&error);
271 return (NULL);
272 }
273
274 /* find volumes and drives among these devices */
275 for (i = 0; i < num_udis; i++) {
276 rmm_dbus_error_free(&error);
277 if (libhal_device_query_capability(hal_ctx, udis[i], "volume",
278 &error)) {
279 v = libhal_volume_from_udi(hal_ctx, udis[i]);
280 if (v != NULL) {
281 *volumes = g_slist_prepend(*volumes, v);
282 }
283 } else if ((*volumes == NULL) &&
284 libhal_device_query_capability(hal_ctx, udis[i], "storage",
285 &error)) {
286 i_drive = i;
287 }
288 }
289
290 if (*volumes != NULL) {
291 /* used prepend, preserve original order */
292 *volumes = g_slist_reverse(*volumes);
293
294 v = (LibHalVolume *)(*volumes)->data;
295 drive = libhal_drive_from_udi(hal_ctx,
296 libhal_volume_get_storage_device_udi(v));
297 if (drive == NULL) {
298 rmm_volumes_free (*volumes);
299 *volumes = NULL;
300 }
301 } else if (i_drive >= 0) {
302 drive = libhal_drive_from_udi(hal_ctx, udis[i_drive]);
303 }
304
305 libhal_free_string_array(udis);
306 rmm_dbus_error_free(&error);
307
308 return (drive);
309 }
310
311 static void
rmm_print_nicknames_one(LibHalDrive * d,LibHalVolume * v,const char * device,char ** drive_nicknames)312 rmm_print_nicknames_one(LibHalDrive *d, LibHalVolume *v,
313 const char *device, char **drive_nicknames)
314 {
315 const char *volume_label = NULL;
316 const char *mount_point = NULL;
317 boolean_t comma;
318 int i;
319
320 (void) printf("%-*s ", RMM_PRINT_DEVICE_WIDTH, device);
321 comma = B_FALSE;
322
323 if (drive_nicknames != NULL) {
324 for (i = 0; drive_nicknames[i] != NULL; i++) {
325 (void) printf("%s%s", comma ? "," : "",
326 drive_nicknames[i]);
327 comma = B_TRUE;
328 }
329 }
330
331 if ((v != NULL) &&
332 ((volume_label = libhal_volume_get_label(v)) != NULL) &&
333 (strlen(volume_label) > 0)) {
334 (void) printf("%s%s", comma ? "," : "", volume_label);
335 comma = B_TRUE;
336 }
337
338 if ((v != NULL) &&
339 ((mount_point = libhal_volume_get_mount_point(v)) != NULL) &&
340 (strlen(mount_point) > 0)) {
341 (void) printf("%s%s", comma ? "," : "", mount_point);
342 comma = B_TRUE;
343 }
344
345 (void) printf("\n");
346 }
347
348 /*
349 * print nicknames for each available volume
350 *
351 * print_mask:
352 * RMM_PRINT_MOUNTABLE print only mountable volumes
353 * RMM_PRINT_EJECTABLE print volume-less ejectable drives
354 */
355 void
rmm_print_volume_nicknames(LibHalContext * hal_ctx,DBusError * error,int print_mask)356 rmm_print_volume_nicknames(LibHalContext *hal_ctx, DBusError *error,
357 int print_mask)
358 {
359 char **udis;
360 int num_udis;
361 GSList *volumes = NULL;
362 LibHalDrive *d, *d_tmp;
363 LibHalVolume *v;
364 const char *device;
365 char **nicknames;
366 int i;
367 GSList *j;
368 int nprinted;
369
370 dbus_error_init(error);
371
372 if ((udis = libhal_find_device_by_capability(hal_ctx, "storage",
373 &num_udis, error)) == NULL) {
374 rmm_dbus_error_free(error);
375 return;
376 }
377
378 for (i = 0; i < num_udis; i++) {
379 if ((d = libhal_drive_from_udi(hal_ctx, udis[i])) == NULL) {
380 continue;
381 }
382
383 /* find volumes belonging to this drive */
384 if ((d_tmp = rmm_hal_volume_findby(hal_ctx,
385 "block.storage_device", udis[i], &volumes)) != NULL) {
386 libhal_drive_free(d_tmp);
387 }
388
389 nicknames = libhal_device_get_property_strlist(hal_ctx,
390 udis[i], "storage.solaris.nicknames", NULL);
391
392 nprinted = 0;
393 for (j = volumes; j != NULL; j = g_slist_next(j)) {
394 v = (LibHalVolume *)(j->data);
395
396 if ((device = libhal_volume_get_device_file(v)) ==
397 NULL) {
398 continue;
399 }
400 if ((print_mask & RMM_PRINT_MOUNTABLE) &&
401 (libhal_volume_get_fsusage(v) !=
402 LIBHAL_VOLUME_USAGE_MOUNTABLE_FILESYSTEM)) {
403 continue;
404 }
405
406 rmm_print_nicknames_one(d, v, device, nicknames);
407 nprinted++;
408 }
409
410 if ((nprinted == 0) &&
411 (print_mask & RMM_PRINT_EJECTABLE) &&
412 libhal_drive_requires_eject(d) &&
413 ((device = libhal_drive_get_device_file(d)) != NULL)) {
414 rmm_print_nicknames_one(d, NULL, device, nicknames);
415 }
416
417 libhal_free_string_array(nicknames);
418 libhal_drive_free(d);
419 rmm_volumes_free(volumes);
420 volumes = NULL;
421 }
422
423 libhal_free_string_array(udis);
424 }
425
426 /*
427 * find volume by nickname
428 * returns the LibHalDrive object and a list of LibHalVolume objects.
429 */
430 LibHalDrive *
rmm_hal_volume_findby_nickname(LibHalContext * hal_ctx,const char * name,GSList ** volumes)431 rmm_hal_volume_findby_nickname(LibHalContext *hal_ctx, const char *name,
432 GSList **volumes)
433 {
434 DBusError error;
435 LibHalDrive *drive = NULL;
436 LibHalDrive *drive_tmp;
437 char **udis;
438 int num_udis;
439 char **nicknames;
440 int i, j;
441
442 *volumes = NULL;
443
444 dbus_error_init(&error);
445
446 if ((udis = libhal_find_device_by_capability(hal_ctx, "storage",
447 &num_udis, &error)) == NULL) {
448 rmm_dbus_error_free(&error);
449 return (NULL);
450 }
451
452 /* find a drive by nickname */
453 for (i = 0; (i < num_udis) && (drive == NULL); i++) {
454 if ((nicknames = libhal_device_get_property_strlist(hal_ctx,
455 udis[i], "storage.solaris.nicknames", &error)) == NULL) {
456 rmm_dbus_error_free(&error);
457 continue;
458 }
459 for (j = 0; (nicknames[j] != NULL) && (drive == NULL); j++) {
460 if (strcmp(nicknames[j], name) == 0) {
461 drive = libhal_drive_from_udi(hal_ctx, udis[i]);
462 }
463 }
464 libhal_free_string_array(nicknames);
465 }
466 libhal_free_string_array(udis);
467
468 if (drive != NULL) {
469 /* found the drive, now find its volumes */
470 if ((drive_tmp = rmm_hal_volume_findby(hal_ctx,
471 "block.storage_device", libhal_drive_get_udi(drive),
472 volumes)) != NULL) {
473 libhal_drive_free(drive_tmp);
474 }
475 }
476
477 rmm_dbus_error_free(&error);
478
479 return (drive);
480 }
481
482 void
rmm_volumes_free(GSList * volumes)483 rmm_volumes_free(GSList *volumes)
484 {
485 GSList *i;
486
487 for (i = volumes; i != NULL; i = g_slist_next(i)) {
488 libhal_volume_free((LibHalVolume *)(i->data));
489 }
490 g_slist_free(volumes);
491 }
492
493 /*
494 * Call HAL's Mount() method on the given device
495 */
496 boolean_t
rmm_hal_mount(LibHalContext * hal_ctx,const char * udi,char ** opts,int num_opts,char * mountpoint,DBusError * error)497 rmm_hal_mount(LibHalContext *hal_ctx, const char *udi,
498 char **opts, int num_opts, char *mountpoint, DBusError *error)
499 {
500 DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
501 DBusMessage *dmesg, *reply;
502 char *fstype;
503
504 dbgprintf("mounting %s...\n", udi);
505
506 if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal", udi,
507 "org.freedesktop.Hal.Device.Volume", "Mount"))) {
508 dbgprintf(
509 "mount failed for %s: cannot create dbus message\n", udi);
510 return (B_FALSE);
511 }
512
513 fstype = "";
514 if (mountpoint == NULL) {
515 mountpoint = "";
516 }
517
518 if (!dbus_message_append_args(dmesg, DBUS_TYPE_STRING, &mountpoint,
519 DBUS_TYPE_STRING, &fstype,
520 DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &opts, num_opts,
521 DBUS_TYPE_INVALID)) {
522 dbgprintf("mount failed for %s: cannot append args\n", udi);
523 dbus_message_unref(dmesg);
524 return (B_FALSE);
525 }
526
527 dbus_error_init(error);
528 if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
529 dmesg, RMM_MOUNT_TIMEOUT, error))) {
530 dbgprintf("mount failed for %s: %s\n", udi, error->message);
531 dbus_message_unref(dmesg);
532 return (B_FALSE);
533 }
534
535 dbgprintf("mounted %s\n", udi);
536
537 dbus_message_unref(dmesg);
538 dbus_message_unref(reply);
539
540 rmm_dbus_error_free(error);
541
542 return (B_TRUE);
543 }
544
545
546 /*
547 * Call HAL's Unmount() method on the given device
548 */
549 boolean_t
rmm_hal_unmount(LibHalContext * hal_ctx,const char * udi,DBusError * error)550 rmm_hal_unmount(LibHalContext *hal_ctx, const char *udi, DBusError *error)
551 {
552 DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
553 DBusMessage *dmesg, *reply;
554 char **opts = NULL;
555
556 dbgprintf("unmounting %s...\n", udi);
557
558 if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal", udi,
559 "org.freedesktop.Hal.Device.Volume", "Unmount"))) {
560 dbgprintf(
561 "unmount failed %s: cannot create dbus message\n", udi);
562 return (B_FALSE);
563 }
564
565 if (!dbus_message_append_args(dmesg, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
566 &opts, 0, DBUS_TYPE_INVALID)) {
567 dbgprintf("unmount failed %s: cannot append args\n", udi);
568 dbus_message_unref(dmesg);
569 return (B_FALSE);
570 }
571
572 dbus_error_init(error);
573 if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
574 dmesg, RMM_UNMOUNT_TIMEOUT, error))) {
575 dbgprintf("unmount failed for %s: %s\n", udi, error->message);
576 dbus_message_unref(dmesg);
577 return (B_FALSE);
578 }
579
580 dbgprintf("unmounted %s\n", udi);
581
582 dbus_message_unref(dmesg);
583 dbus_message_unref(reply);
584
585 rmm_dbus_error_free(error);
586
587 return (B_TRUE);
588 }
589
590
591 /*
592 * Call HAL's Eject() method on the given device
593 */
594 boolean_t
rmm_hal_eject(LibHalContext * hal_ctx,const char * udi,DBusError * error)595 rmm_hal_eject(LibHalContext *hal_ctx, const char *udi, DBusError *error)
596 {
597 DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
598 DBusMessage *dmesg, *reply;
599 char **options = NULL;
600 uint_t num_options = 0;
601
602 dbgprintf("ejecting %s...\n", udi);
603
604 if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal", udi,
605 "org.freedesktop.Hal.Device.Storage", "Eject"))) {
606 dbgprintf("eject %s: cannot create dbus message\n", udi);
607 return (B_FALSE);
608 }
609
610 if (!dbus_message_append_args(dmesg,
611 DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options, num_options,
612 DBUS_TYPE_INVALID)) {
613 dbgprintf("eject %s: cannot append args to dbus message ", udi);
614 dbus_message_unref(dmesg);
615 return (B_FALSE);
616 }
617
618 dbus_error_init(error);
619 if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
620 dmesg, RMM_EJECT_TIMEOUT, error))) {
621 dbgprintf("eject %s: %s\n", udi, error->message);
622 dbus_message_unref(dmesg);
623 return (B_FALSE);
624 }
625
626 dbgprintf("ejected %s\n", udi);
627
628 dbus_message_unref(dmesg);
629 dbus_message_unref(reply);
630
631 rmm_dbus_error_free(error);
632
633 return (B_TRUE);
634 }
635
636 /*
637 * Call HAL's CloseTray() method on the given device
638 */
639 boolean_t
rmm_hal_closetray(LibHalContext * hal_ctx,const char * udi,DBusError * error)640 rmm_hal_closetray(LibHalContext *hal_ctx, const char *udi, DBusError *error)
641 {
642 DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
643 DBusMessage *dmesg, *reply;
644 char **options = NULL;
645 uint_t num_options = 0;
646
647 dbgprintf("closing tray %s...\n", udi);
648
649 if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal", udi,
650 "org.freedesktop.Hal.Device.Storage", "CloseTray"))) {
651 dbgprintf(
652 "closetray failed for %s: cannot create dbus message\n",
653 udi);
654 return (B_FALSE);
655 }
656
657 if (!dbus_message_append_args(dmesg,
658 DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options, num_options,
659 DBUS_TYPE_INVALID)) {
660 dbgprintf("closetray %s: cannot append args to dbus message ",
661 udi);
662 dbus_message_unref(dmesg);
663 return (B_FALSE);
664 }
665
666 dbus_error_init(error);
667 if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
668 dmesg, RMM_CLOSETRAY_TIMEOUT, error))) {
669 dbgprintf("closetray failed for %s: %s\n", udi, error->message);
670 dbus_message_unref(dmesg);
671 return (B_FALSE);
672 }
673
674 dbgprintf("closetray ok %s\n", udi);
675
676 dbus_message_unref(dmesg);
677 dbus_message_unref(reply);
678
679 rmm_dbus_error_free(error);
680
681 return (B_TRUE);
682 }
683
684 /*
685 * Call HAL's Rescan() method on the given device
686 */
687 boolean_t
rmm_hal_rescan(LibHalContext * hal_ctx,const char * udi,DBusError * error)688 rmm_hal_rescan(LibHalContext *hal_ctx, const char *udi, DBusError *error)
689 {
690 DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
691 DBusMessage *dmesg, *reply;
692
693 dbgprintf("rescanning %s...\n", udi);
694
695 if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal", udi,
696 "org.freedesktop.Hal.Device", "Rescan"))) {
697 dbgprintf("rescan failed for %s: cannot create dbus message\n",
698 udi);
699 return (B_FALSE);
700 }
701
702 dbus_error_init(error);
703 if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
704 dmesg, -1, error))) {
705 dbgprintf("rescan failed for %s: %s\n", udi, error->message);
706 dbus_message_unref(dmesg);
707 return (B_FALSE);
708 }
709
710 dbgprintf("rescan ok %s\n", udi);
711
712 dbus_message_unref(dmesg);
713 dbus_message_unref(reply);
714
715 rmm_dbus_error_free(error);
716
717 return (B_TRUE);
718 }
719
720 boolean_t
rmm_hal_claim_branch(LibHalContext * hal_ctx,const char * udi)721 rmm_hal_claim_branch(LibHalContext *hal_ctx, const char *udi)
722 {
723 DBusError error;
724 DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
725 DBusMessage *dmesg, *reply;
726 const char *claimed_by = "rmvolmgr";
727
728 dbgprintf("claiming branch %s...\n", udi);
729
730 if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal",
731 "/org/freedesktop/Hal/Manager", "org.freedesktop.Hal.Manager",
732 "ClaimBranch"))) {
733 dbgprintf("cannot create dbus message\n");
734 return (B_FALSE);
735 }
736
737 if (!dbus_message_append_args(dmesg, DBUS_TYPE_STRING, &udi,
738 DBUS_TYPE_STRING, &claimed_by, DBUS_TYPE_INVALID)) {
739 dbgprintf("cannot append args to dbus message\n");
740 dbus_message_unref(dmesg);
741 return (B_FALSE);
742 }
743
744 dbus_error_init(&error);
745 if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
746 dmesg, -1, &error))) {
747 dbgprintf("cannot send dbus message\n");
748 dbus_message_unref(dmesg);
749 rmm_dbus_error_free(&error);
750 return (B_FALSE);
751 }
752
753 dbgprintf("claim branch ok %s\n", udi);
754
755 dbus_message_unref(dmesg);
756 dbus_message_unref(reply);
757
758 return (B_TRUE);
759 }
760
761 boolean_t
rmm_hal_unclaim_branch(LibHalContext * hal_ctx,const char * udi)762 rmm_hal_unclaim_branch(LibHalContext *hal_ctx, const char *udi)
763 {
764 DBusError error;
765 DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
766 DBusMessage *dmesg, *reply;
767 const char *claimed_by = "rmvolmgr";
768
769 dbgprintf("unclaiming branch %s...\n", udi);
770
771 if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal",
772 "/org/freedesktop/Hal/Manager", "org.freedesktop.Hal.Manager",
773 "UnclaimBranch"))) {
774 dbgprintf("cannot create dbus message\n");
775 return (B_FALSE);
776 }
777
778 if (!dbus_message_append_args(dmesg, DBUS_TYPE_STRING, &udi,
779 DBUS_TYPE_STRING, &claimed_by, DBUS_TYPE_INVALID)) {
780 dbgprintf("cannot append args to dbus message\n");
781 dbus_message_unref(dmesg);
782 return (B_FALSE);
783 }
784
785 dbus_error_init(&error);
786 if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
787 dmesg, -1, &error))) {
788 dbgprintf("cannot send dbus message\n");
789 dbus_message_unref(dmesg);
790 rmm_dbus_error_free(&error);
791 return (B_FALSE);
792 }
793
794 dbgprintf("unclaim branch ok %s\n", udi);
795
796 dbus_message_unref(dmesg);
797 dbus_message_unref(reply);
798
799 return (B_TRUE);
800 }
801
802 static boolean_t
rmm_action_one(LibHalContext * hal_ctx,const char * name,action_t action,const char * dev,const char * udi,LibHalVolume * v,char ** opts,int num_opts,char * mountpoint)803 rmm_action_one(LibHalContext *hal_ctx, const char *name, action_t action,
804 const char *dev, const char *udi, LibHalVolume *v,
805 char **opts, int num_opts, char *mountpoint)
806 {
807 char dev_str[MAXPATHLEN];
808 char *mountp;
809 DBusError error;
810 boolean_t ret = B_FALSE;
811
812 if (strcmp(name, dev) == 0) {
813 (void) snprintf(dev_str, sizeof (dev_str), name);
814 } else {
815 (void) snprintf(dev_str, sizeof (dev_str), "%s %s", name, dev);
816 }
817
818 dbus_error_init(&error);
819
820 switch (action) {
821 case EJECT:
822 ret = rmm_hal_eject(hal_ctx, udi, &error);
823 break;
824 case INSERT:
825 case REMOUNT:
826 if (libhal_volume_is_mounted(v)) {
827 goto done;
828 }
829 ret = rmm_hal_mount(hal_ctx, udi,
830 opts, num_opts, mountpoint, &error);
831 break;
832 case UNMOUNT:
833 if (!libhal_volume_is_mounted(v)) {
834 goto done;
835 }
836 ret = rmm_hal_unmount(hal_ctx, udi, &error);
837 break;
838 case CLOSETRAY:
839 ret = rmm_hal_closetray(hal_ctx, udi, &error);
840 break;
841 }
842
843 if (!ret) {
844 (void) fprintf(stderr, gettext("%s of %s failed: %s\n"),
845 action_strings[action], dev_str, rmm_strerror(&error, -1));
846 goto done;
847 }
848
849 switch (action) {
850 case EJECT:
851 (void) printf(gettext("%s ejected\n"), dev_str);
852 break;
853 case INSERT:
854 case REMOUNT:
855 mountp = rmm_get_mnttab_mount_point(dev);
856 if (mountp != NULL) {
857 (void) printf(gettext("%s mounted at %s\n"),
858 dev_str, mountp);
859 free(mountp);
860 }
861 break;
862 case UNMOUNT:
863 (void) printf(gettext("%s unmounted\n"), dev_str);
864 break;
865 case CLOSETRAY:
866 (void) printf(gettext("%s tray closed\n"), dev_str);
867 break;
868 }
869
870 done:
871 rmm_dbus_error_free(&error);
872 return (ret);
873 }
874
875 /*
876 * top level action routine
877 *
878 * If non-null 'aa' is passed, it will be used, otherwise a local copy
879 * will be created.
880 */
881 boolean_t
rmm_action(LibHalContext * hal_ctx,const char * name,action_t action,struct action_arg * aap,char ** opts,int num_opts,char * mountpoint)882 rmm_action(LibHalContext *hal_ctx, const char *name, action_t action,
883 struct action_arg *aap, char **opts, int num_opts, char *mountpoint)
884 {
885 DBusError error;
886 GSList *volumes, *i;
887 LibHalDrive *d;
888 LibHalVolume *v;
889 const char *udi, *d_udi;
890 const char *dev, *d_dev;
891 struct action_arg aa_local;
892 boolean_t ret = B_FALSE;
893
894 dbgprintf("rmm_action %s %s\n", name, action_strings[action]);
895
896 if (aap == NULL) {
897 bzero(&aa_local, sizeof (aa_local));
898 aap = &aa_local;
899 }
900
901 dbus_error_init(&error);
902
903 /* find the drive and its volumes */
904 d = rmm_hal_volume_find(hal_ctx, name, &error, &volumes);
905 rmm_dbus_error_free(&error);
906 if (d == NULL) {
907 (void) fprintf(stderr, gettext("cannot find '%s'\n"), name);
908 return (B_FALSE);
909 }
910 d_udi = libhal_drive_get_udi(d);
911 d_dev = libhal_drive_get_device_file(d);
912 if ((d_udi == NULL) || (d_dev == NULL)) {
913 goto out;
914 }
915
916 /*
917 * For those drives that do not require media eject,
918 * EJECT turns into UNMOUNT.
919 */
920 if ((action == EJECT) && !libhal_drive_requires_eject(d)) {
921 action = UNMOUNT;
922 }
923
924 /* per drive action */
925 if ((action == EJECT) || (action == CLOSETRAY)) {
926 ret = rmm_action_one(hal_ctx, name, action, d_dev, d_udi, NULL,
927 opts, num_opts, NULL);
928
929 if (!ret || (action == CLOSETRAY)) {
930 goto out;
931 }
932 }
933
934 /* per volume action */
935 for (i = volumes; i != NULL; i = g_slist_next(i)) {
936 v = (LibHalVolume *)i->data;
937 udi = libhal_volume_get_udi(v);
938 dev = libhal_volume_get_device_file(v);
939
940 if ((udi == NULL) || (dev == NULL)) {
941 continue;
942 }
943 if (aap == &aa_local) {
944 if (!rmm_volume_aa_from_prop(hal_ctx, udi, v, aap)) {
945 dbgprintf("rmm_volume_aa_from_prop failed %s\n",
946 udi);
947 continue;
948 }
949 }
950 aap->aa_action = action;
951
952 /* ejected above, just need postprocess */
953 if (action != EJECT) {
954 ret = rmm_action_one(hal_ctx, name, action, dev, udi, v,
955 opts, num_opts, mountpoint);
956 }
957 if (ret) {
958 (void) vold_postprocess(hal_ctx, udi, aap);
959 }
960
961 if (aap == &aa_local) {
962 rmm_volume_aa_free(aap);
963 }
964 }
965
966 out:
967 rmm_volumes_free(volumes);
968 libhal_drive_free(d);
969
970 return (ret);
971 }
972
973
974 /*
975 * rescan by name
976 * if name is NULL, rescan all drives
977 */
978 boolean_t
rmm_rescan(LibHalContext * hal_ctx,const char * name,boolean_t query)979 rmm_rescan(LibHalContext *hal_ctx, const char *name, boolean_t query)
980 {
981 DBusError error;
982 GSList *volumes;
983 LibHalDrive *drive = NULL;
984 const char *drive_udi;
985 char **udis;
986 int num_udis;
987 char *nickname;
988 char **nicks = NULL;
989 boolean_t do_free_udis = FALSE;
990 int i;
991 boolean_t ret = B_FALSE;
992
993 dbgprintf("rmm_rescan %s\n", name != NULL ? name : "all");
994
995 dbus_error_init(&error);
996
997 if (name != NULL) {
998 if ((drive = rmm_hal_volume_find(hal_ctx, name, &error,
999 &volumes)) == NULL) {
1000 rmm_dbus_error_free(&error);
1001 (void) fprintf(stderr,
1002 gettext("cannot find '%s'\n"), name);
1003 return (B_FALSE);
1004 }
1005 rmm_dbus_error_free(&error);
1006 g_slist_free(volumes);
1007
1008 drive_udi = libhal_drive_get_udi(drive);
1009 udis = (char **)&drive_udi;
1010 num_udis = 1;
1011 } else {
1012 if ((udis = libhal_find_device_by_capability(hal_ctx,
1013 "storage", &num_udis, &error)) == NULL) {
1014 rmm_dbus_error_free(&error);
1015 return (B_TRUE);
1016 }
1017 rmm_dbus_error_free(&error);
1018 do_free_udis = TRUE;
1019 }
1020
1021 for (i = 0; i < num_udis; i++) {
1022 if (name == NULL) {
1023 nicks = libhal_device_get_property_strlist(hal_ctx,
1024 udis[i], "storage.solaris.nicknames", NULL);
1025 if (nicks != NULL) {
1026 nickname = nicks[0];
1027 } else {
1028 nickname = "";
1029 }
1030 }
1031 if (!(ret = rmm_hal_rescan(hal_ctx, udis[i], &error))) {
1032 (void) fprintf(stderr,
1033 gettext("rescan of %s failed: %s\n"),
1034 name ? name : nickname,
1035 rmm_strerror(&error, -1));
1036 libhal_free_string_array(nicks);
1037 continue;
1038 }
1039 if (query) {
1040 ret = libhal_device_get_property_bool(hal_ctx, udis[i],
1041 "storage.removable.media_available", NULL);
1042 if (ret) {
1043 printf(gettext("%s is available\n"),
1044 name ? name : nickname);
1045 } else {
1046 printf(gettext("%s is not available\n"),
1047 name ? name : nickname);
1048 }
1049 }
1050 libhal_free_string_array(nicks);
1051 }
1052
1053 if (drive != NULL) {
1054 libhal_drive_free(drive);
1055 }
1056 if (do_free_udis) {
1057 libhal_free_string_array(udis);
1058 }
1059
1060 return (ret);
1061 }
1062
1063
1064 /*
1065 * set action_arg from volume properties
1066 */
1067 boolean_t
rmm_volume_aa_from_prop(LibHalContext * hal_ctx,const char * udi_arg,LibHalVolume * volume_arg,struct action_arg * aap)1068 rmm_volume_aa_from_prop(LibHalContext *hal_ctx, const char *udi_arg,
1069 LibHalVolume *volume_arg, struct action_arg *aap)
1070 {
1071 LibHalVolume *volume = volume_arg;
1072 const char *udi = udi_arg;
1073 const char *drive_udi;
1074 char *volume_label;
1075 char *mountpoint;
1076 int len;
1077 int ret = B_FALSE;
1078
1079 /* at least udi or volume must be supplied */
1080 if ((udi == NULL) && (volume == NULL)) {
1081 return (B_FALSE);
1082 }
1083 if (volume == NULL) {
1084 if ((volume = libhal_volume_from_udi(hal_ctx, udi)) == NULL) {
1085 dbgprintf("cannot get volume %s\n", udi);
1086 goto out;
1087 }
1088 }
1089 if (udi == NULL) {
1090 if ((udi = libhal_volume_get_udi(volume)) == NULL) {
1091 dbgprintf("cannot get udi\n");
1092 goto out;
1093 }
1094 }
1095 drive_udi = libhal_volume_get_storage_device_udi(volume);
1096
1097 if (!(aap->aa_symdev = libhal_device_get_property_string(hal_ctx,
1098 drive_udi, "storage.solaris.legacy.symdev", NULL))) {
1099 dbgprintf("property %s not found %s\n",
1100 "storage.solaris.legacy.symdev", drive_udi);
1101 goto out;
1102 }
1103 if (!(aap->aa_media = libhal_device_get_property_string(hal_ctx,
1104 drive_udi, "storage.solaris.legacy.media_type", NULL))) {
1105 dbgprintf("property %s not found %s\n",
1106 "storage.solaris.legacy.media_type", drive_udi);
1107 goto out;
1108 }
1109
1110 /* name is derived from volume label */
1111 aap->aa_name = NULL;
1112 if ((volume_label = (char *)libhal_device_get_property_string(hal_ctx,
1113 udi, "volume.label", NULL)) != NULL) {
1114 if ((len = strlen(volume_label)) > 0) {
1115 aap->aa_name = rmm_vold_convert_volume_label(
1116 volume_label, len);
1117 if (strlen(aap->aa_name) == 0) {
1118 free(aap->aa_name);
1119 aap->aa_name = NULL;
1120 }
1121 }
1122 libhal_free_string(volume_label);
1123 }
1124 /* if no label, then unnamed_<mediatype> */
1125 if (aap->aa_name == NULL) {
1126 aap->aa_name = (char *)calloc(1, sizeof ("unnamed_floppyNNNN"));
1127 if (aap->aa_name == NULL) {
1128 goto out;
1129 }
1130 (void) snprintf(aap->aa_name, sizeof ("unnamed_floppyNNNN"),
1131 "unnamed_%s", aap->aa_media);
1132 }
1133
1134 if (!(aap->aa_path = libhal_device_get_property_string(hal_ctx, udi,
1135 "block.device", NULL))) {
1136 dbgprintf("property %s not found %s\n", "block.device", udi);
1137 goto out;
1138 }
1139 if (!(aap->aa_rawpath = libhal_device_get_property_string(hal_ctx, udi,
1140 "block.solaris.raw_device", NULL))) {
1141 dbgprintf("property %s not found %s\n",
1142 "block.solaris.raw_device", udi);
1143 goto out;
1144 }
1145 if (!(aap->aa_type = libhal_device_get_property_string(hal_ctx, udi,
1146 "volume.fstype", NULL))) {
1147 dbgprintf("property %s not found %s\n", "volume.fstype", udi);
1148 goto out;
1149 }
1150 if (!libhal_device_get_property_bool(hal_ctx, udi,
1151 "volume.is_partition", NULL)) {
1152 aap->aa_partname = NULL;
1153 } else if (!(aap->aa_partname = libhal_device_get_property_string(
1154 hal_ctx, udi, "block.solaris.slice", NULL))) {
1155 dbgprintf("property %s not found %s\n",
1156 "block.solaris.slice", udi);
1157 goto out;
1158 }
1159 if (!(mountpoint = libhal_device_get_property_string(hal_ctx, udi,
1160 "volume.mount_point", NULL))) {
1161 dbgprintf("property %s not found %s\n",
1162 "volume.mount_point", udi);
1163 goto out;
1164 }
1165 /*
1166 * aa_mountpoint can be reallocated in rmm_volume_aa_update_mountpoint()
1167 * won't have to choose between free() or libhal_free_string() later on
1168 */
1169 aap->aa_mountpoint = strdup(mountpoint);
1170 libhal_free_string(mountpoint);
1171 if (aap->aa_mountpoint == NULL) {
1172 dbgprintf("mountpoint is NULL %s\n", udi);
1173 goto out;
1174 }
1175
1176 ret = B_TRUE;
1177
1178 out:
1179 if ((volume != NULL) && (volume != volume_arg)) {
1180 libhal_volume_free(volume);
1181 }
1182 if (!ret) {
1183 rmm_volume_aa_free(aap);
1184 }
1185 return (ret);
1186 }
1187
1188 /* ARGSUSED */
1189 void
rmm_volume_aa_update_mountpoint(LibHalContext * hal_ctx,const char * udi,struct action_arg * aap)1190 rmm_volume_aa_update_mountpoint(LibHalContext *hal_ctx, const char *udi,
1191 struct action_arg *aap)
1192 {
1193 if (aap->aa_mountpoint != NULL) {
1194 free(aap->aa_mountpoint);
1195 }
1196 aap->aa_mountpoint = rmm_get_mnttab_mount_point(aap->aa_path);
1197 }
1198
1199 void
rmm_volume_aa_free(struct action_arg * aap)1200 rmm_volume_aa_free(struct action_arg *aap)
1201 {
1202 if (aap->aa_symdev != NULL) {
1203 libhal_free_string(aap->aa_symdev);
1204 aap->aa_symdev = NULL;
1205 }
1206 if (aap->aa_name != NULL) {
1207 free(aap->aa_name);
1208 aap->aa_name = NULL;
1209 }
1210 if (aap->aa_path != NULL) {
1211 libhal_free_string(aap->aa_path);
1212 aap->aa_path = NULL;
1213 }
1214 if (aap->aa_rawpath != NULL) {
1215 libhal_free_string(aap->aa_rawpath);
1216 aap->aa_rawpath = NULL;
1217 }
1218 if (aap->aa_type != NULL) {
1219 libhal_free_string(aap->aa_type);
1220 aap->aa_type = NULL;
1221 }
1222 if (aap->aa_media != NULL) {
1223 libhal_free_string(aap->aa_media);
1224 aap->aa_media = NULL;
1225 }
1226 if (aap->aa_partname != NULL) {
1227 libhal_free_string(aap->aa_partname);
1228 aap->aa_partname = NULL;
1229 }
1230 if (aap->aa_mountpoint != NULL) {
1231 free(aap->aa_mountpoint);
1232 aap->aa_mountpoint = NULL;
1233 }
1234 }
1235
1236 /*
1237 * get device's mount point from mnttab
1238 */
1239 char *
rmm_get_mnttab_mount_point(const char * special)1240 rmm_get_mnttab_mount_point(const char *special)
1241 {
1242 char *mount_point = NULL;
1243 FILE *f;
1244 struct mnttab mnt;
1245 struct mnttab mpref = { NULL, NULL, NULL, NULL, NULL };
1246
1247 if ((f = fopen(MNTTAB, "r")) != NULL) {
1248 mpref.mnt_special = (char *)special;
1249 if (getmntany(f, &mnt, &mpref) == 0) {
1250 mount_point = strdup(mnt.mnt_mountp);
1251 }
1252 fclose(f);
1253 }
1254
1255 return (mount_point);
1256 }
1257
1258
1259 /*
1260 * get human readable string from error values
1261 */
1262 const char *
rmm_strerror(DBusError * dbus_error,int rmm_error)1263 rmm_strerror(DBusError *dbus_error, int rmm_error)
1264 {
1265 const char *str;
1266
1267 if ((dbus_error != NULL) && dbus_error_is_set(dbus_error)) {
1268 str = dbus_error->message;
1269 } else {
1270 switch (rmm_error) {
1271 case RMM_EOK:
1272 str = gettext("success");
1273 break;
1274 case RMM_EDBUS_CONNECT:
1275 str = gettext("cannot connect to D-Bus");
1276 break;
1277 case RMM_EHAL_CONNECT:
1278 str = gettext("cannot connect to HAL");
1279 break;
1280 default:
1281 str = gettext("undefined error");
1282 break;
1283 }
1284 }
1285
1286 return (str);
1287 }
1288
1289 void
rmm_dbus_error_free(DBusError * error)1290 rmm_dbus_error_free(DBusError *error)
1291 {
1292 if (error != NULL && dbus_error_is_set(error)) {
1293 dbus_error_free(error);
1294 }
1295 }
1296
1297 static int
rmm_vold_isbadchar(int c)1298 rmm_vold_isbadchar(int c)
1299 {
1300 int ret_val = 0;
1301
1302
1303 switch (c) {
1304 case '/':
1305 case ';':
1306 case '|':
1307 ret_val = 1;
1308 break;
1309 default:
1310 if (iscntrl(c) || isspace(c)) {
1311 ret_val = 1;
1312 }
1313 }
1314
1315 return (ret_val);
1316 }
1317
1318 char *
rmm_vold_convert_volume_label(const char * name,size_t len)1319 rmm_vold_convert_volume_label(const char *name, size_t len)
1320 {
1321 char buf[MAXNAMELEN+1];
1322 char *s = buf;
1323 int i;
1324
1325 if (len > MAXNAMELEN) {
1326 len = MAXNAMELEN;
1327 }
1328
1329 for (i = 0; i < len; i++) {
1330 if (name[i] == '\0') {
1331 break;
1332 }
1333 if (isgraph((int)name[i])) {
1334 if (isupper((int)name[i])) {
1335 *s++ = tolower((int)name[i]);
1336 } else if (rmm_vold_isbadchar((int)name[i])) {
1337 *s++ = '_';
1338 } else {
1339 *s++ = name[i];
1340 }
1341 }
1342 }
1343 *s = '\0';
1344 s = strdup(buf);
1345
1346 return (s);
1347 }
1348
1349 /*
1350 * swiped from mkdir.c
1351 */
1352 int
makepath(char * dir,mode_t mode)1353 makepath(char *dir, mode_t mode)
1354 {
1355 int err;
1356 char *slash;
1357
1358
1359 if ((mkdir(dir, mode) == 0) || (errno == EEXIST)) {
1360 return (0);
1361 }
1362 if (errno != ENOENT) {
1363 return (-1);
1364 }
1365 if ((slash = strrchr(dir, '/')) == NULL) {
1366 return (-1);
1367 }
1368 *slash = '\0';
1369 err = makepath(dir, mode);
1370 *slash++ = '/';
1371
1372 if (err || (*slash == '\0')) {
1373 return (err);
1374 }
1375
1376 return (mkdir(dir, mode));
1377 }
1378
1379
1380 void
dbgprintf(const char * fmt,...)1381 dbgprintf(const char *fmt, ...)
1382 {
1383
1384 va_list ap;
1385 const char *p;
1386 char msg[BUFSIZ];
1387 char *errmsg = strerror(errno);
1388 char *s;
1389
1390 if (rmm_debug == 0) {
1391 return;
1392 }
1393
1394 (void) memset(msg, 0, BUFSIZ);
1395
1396 /* scan for %m and replace with errno msg */
1397 s = &msg[strlen(msg)];
1398 p = fmt;
1399
1400 while (*p != '\0') {
1401 if ((*p == '%') && (*(p+1) == 'm')) {
1402 (void) strcat(s, errmsg);
1403 p += 2;
1404 s += strlen(errmsg);
1405 continue;
1406 }
1407 *s++ = *p++;
1408 }
1409 *s = '\0'; /* don't forget the null byte */
1410
1411 va_start(ap, fmt);
1412 (void) vfprintf(stderr, msg, ap);
1413 va_end(ap);
1414 }
1415