halt.c revision 7656:2621e50fdf4a
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 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27/*	  All Rights Reserved  	*/
28
29/*
30 * University Copyright- Copyright (c) 1982, 1986, 1988
31 * The Regents of the University of California
32 * All Rights Reserved
33 *
34 * University Acknowledgment- Portions of this document are derived from
35 * software developed by the University of California, Berkeley, and its
36 * contributors.
37 */
38
39
40/*
41 * Common code for halt(1M), poweroff(1M), and reboot(1M).  We use
42 * argv[0] to determine which behavior to exhibit.
43 */
44
45#include <stdio.h>
46#include <procfs.h>
47#include <sys/types.h>
48#include <sys/elf.h>
49#include <sys/systeminfo.h>
50#include <sys/stat.h>
51#include <sys/uadmin.h>
52#include <sys/mntent.h>
53#include <sys/mnttab.h>
54#include <sys/mount.h>
55#include <alloca.h>
56#include <assert.h>
57#include <errno.h>
58#include <fcntl.h>
59#include <libgen.h>
60#include <libscf.h>
61#include <limits.h>
62#include <locale.h>
63#include <libintl.h>
64#include <syslog.h>
65#include <signal.h>
66#include <strings.h>
67#include <unistd.h>
68#include <stdlib.h>
69#include <stdio.h>
70#include <strings.h>
71#include <time.h>
72#include <utmpx.h>
73#include <pwd.h>
74#include <zone.h>
75
76#include <libzfs.h>
77
78#if !defined(TEXT_DOMAIN)
79#define	TEXT_DOMAIN	"SYS_TEST"
80#endif
81
82#if defined(__sparc)
83#define	CUR_ELFDATA	ELFDATA2MSB
84#elif defined(__i386)
85#define	CUR_ELFDATA	ELFDATA2LSB
86#endif
87
88static libzfs_handle_t *g_zfs;
89
90extern int audit_halt_setup(int, char **);
91extern int audit_halt_success(void);
92extern int audit_halt_fail(void);
93
94extern int audit_reboot_setup(void);
95extern int audit_reboot_success(void);
96extern int audit_reboot_fail(void);
97
98static char *cmdname;	/* basename(argv[0]), the name of the command */
99
100typedef struct ctidlist_struct {
101	ctid_t ctid;
102	struct ctidlist_struct *next;
103} ctidlist_t;
104
105static ctidlist_t *ctidlist = NULL;
106static ctid_t startdct = -1;
107
108#define	FMRI_STARTD_CONTRACT \
109	"svc:/system/svc/restarter:default/:properties/restarter/contract"
110
111#define	ZONEADM_PROG "/usr/sbin/zoneadm"
112
113/*
114 * The length of FASTBOOT_MOUNTPOINT must be less than MAXPATHLEN.
115 */
116#define	FASTBOOT_MOUNTPOINT	"/tmp/.fastboot.root"
117
118static char	fastboot_mounted[MAXPATHLEN];
119
120static int validate_ufs_disk(char *, char *);
121static int validate_zfs_pool(char *, char *);
122
123static pid_t
124get_initpid()
125{
126	static int init_pid = -1;
127
128	if (init_pid == -1) {
129		if (zone_getattr(getzoneid(), ZONE_ATTR_INITPID, &init_pid,
130		    sizeof (init_pid)) != sizeof (init_pid)) {
131			assert(errno == ESRCH);
132			init_pid = -1;
133		}
134	}
135	return (init_pid);
136}
137
138/*
139 * Quiesce or resume init using /proc.  When stopping init, we can't send
140 * SIGTSTP (since init ignores it) or SIGSTOP (since the kernel won't permit
141 * it).
142 */
143static int
144direct_init(long command)
145{
146	char ctlfile[MAXPATHLEN];
147	pid_t pid;
148	int ctlfd;
149
150	assert(command == PCDSTOP || command == PCRUN);
151	if ((pid = get_initpid()) == -1) {
152		return (-1);
153	}
154
155	(void) snprintf(ctlfile, sizeof (ctlfile), "/proc/%d/ctl", pid);
156	if ((ctlfd = open(ctlfile, O_WRONLY)) == -1)
157		return (-1);
158
159	if (command == PCDSTOP) {
160		if (write(ctlfd, &command, sizeof (long)) == -1) {
161			(void) close(ctlfd);
162			return (-1);
163		}
164	} else {	/* command == PCRUN */
165		long cmds[2];
166		cmds[0] = command;
167		cmds[1] = 0;
168		if (write(ctlfd, cmds, sizeof (cmds)) == -1) {
169			(void) close(ctlfd);
170			return (-1);
171		}
172	}
173	(void) close(ctlfd);
174	return (0);
175}
176
177static void
178stop_startd()
179{
180	scf_handle_t *h;
181	scf_property_t *prop = NULL;
182	scf_value_t *val = NULL;
183	uint64_t uint64;
184
185	if ((h = scf_handle_create(SCF_VERSION)) == NULL)
186		return;
187
188	if ((scf_handle_bind(h) != 0) ||
189	    ((prop = scf_property_create(h)) == NULL) ||
190	    ((val = scf_value_create(h)) == NULL))
191		goto out;
192
193	if (scf_handle_decode_fmri(h, FMRI_STARTD_CONTRACT,
194	    NULL, NULL, NULL, NULL, prop, SCF_DECODE_FMRI_EXACT) != 0)
195		goto out;
196
197	if (scf_property_is_type(prop, SCF_TYPE_COUNT) != 0 ||
198	    scf_property_get_value(prop, val) != 0 ||
199	    scf_value_get_count(val, &uint64) != 0)
200		goto out;
201
202	startdct = (ctid_t)uint64;
203	(void) sigsend(P_CTID, startdct, SIGSTOP);
204
205out:
206	scf_property_destroy(prop);
207	scf_value_destroy(val);
208	scf_handle_destroy(h);
209}
210
211static void
212continue_startd()
213{
214	if (startdct != -1)
215		(void) sigsend(P_CTID, startdct, SIGCONT);
216}
217
218#define	FMRI_RESTARTER_PROP "/:properties/general/restarter"
219#define	FMRI_CONTRACT_PROP "/:properties/restarter/contract"
220
221static int
222save_ctid(ctid_t ctid)
223{
224	ctidlist_t *next;
225
226	for (next = ctidlist; next != NULL; next = next->next)
227		if (next->ctid == ctid)
228			return (-1);
229
230	next = (ctidlist_t *)malloc(sizeof (ctidlist_t));
231	if (next == NULL)
232		return (-1);
233
234	next->ctid = ctid;
235	next->next = ctidlist;
236	ctidlist = next;
237	return (0);
238}
239
240static void
241stop_delegates()
242{
243	ctid_t ctid;
244	scf_handle_t *h;
245	scf_scope_t *sc = NULL;
246	scf_service_t *svc = NULL;
247	scf_instance_t *inst = NULL;
248	scf_snapshot_t *snap = NULL;
249	scf_snapshot_t *isnap = NULL;
250	scf_propertygroup_t *pg = NULL;
251	scf_property_t *prop = NULL;
252	scf_value_t *val = NULL;
253	scf_iter_t *siter = NULL;
254	scf_iter_t *iiter = NULL;
255	char *fmri;
256	ssize_t length;
257
258	uint64_t uint64;
259	ssize_t bytes;
260
261	length = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
262	if (length <= 0)
263		return;
264
265	length++;
266	fmri = alloca(length * sizeof (char));
267
268	if ((h = scf_handle_create(SCF_VERSION)) == NULL)
269		return;
270
271	if (scf_handle_bind(h) != 0) {
272		scf_handle_destroy(h);
273		return;
274	}
275
276	if ((sc = scf_scope_create(h)) == NULL ||
277	    (svc = scf_service_create(h)) == NULL ||
278	    (inst = scf_instance_create(h)) == NULL ||
279	    (snap = scf_snapshot_create(h)) == NULL ||
280	    (pg = scf_pg_create(h)) == NULL ||
281	    (prop = scf_property_create(h)) == NULL ||
282	    (val = scf_value_create(h)) == NULL ||
283	    (siter = scf_iter_create(h)) == NULL ||
284	    (iiter = scf_iter_create(h)) == NULL)
285		goto out;
286
287	if (scf_handle_get_scope(h, SCF_SCOPE_LOCAL, sc) != 0)
288		goto out;
289
290	if (scf_iter_scope_services(siter, sc) != 0)
291		goto out;
292
293	while (scf_iter_next_service(siter, svc) == 1) {
294
295		if (scf_iter_service_instances(iiter, svc) != 0)
296			continue;
297
298		while (scf_iter_next_instance(iiter, inst) == 1) {
299
300			if ((scf_instance_get_snapshot(inst, "running",
301			    snap)) != 0)
302				isnap = NULL;
303			else
304				isnap = snap;
305
306			if (scf_instance_get_pg_composed(inst, isnap,
307			    SCF_PG_GENERAL, pg) != 0)
308				continue;
309
310			if (scf_pg_get_property(pg, SCF_PROPERTY_RESTARTER,
311			    prop) != 0 ||
312			    scf_property_get_value(prop, val) != 0)
313				continue;
314
315			bytes = scf_value_get_astring(val, fmri, length);
316			if (bytes <= 0 || bytes >= length)
317				continue;
318
319			if (strlcat(fmri, FMRI_CONTRACT_PROP, length) >=
320			    length)
321				continue;
322
323			if (scf_handle_decode_fmri(h, fmri, NULL, NULL,
324			    NULL, NULL, prop, SCF_DECODE_FMRI_EXACT) != 0)
325				continue;
326
327			if (scf_property_is_type(prop, SCF_TYPE_COUNT) != 0 ||
328			    scf_property_get_value(prop, val) != 0 ||
329			    scf_value_get_count(val, &uint64) != 0)
330				continue;
331
332			ctid = (ctid_t)uint64;
333			if (save_ctid(ctid) == 0) {
334				(void) sigsend(P_CTID, ctid, SIGSTOP);
335			}
336		}
337	}
338out:
339	scf_scope_destroy(sc);
340	scf_service_destroy(svc);
341	scf_instance_destroy(inst);
342	scf_snapshot_destroy(snap);
343	scf_pg_destroy(pg);
344	scf_property_destroy(prop);
345	scf_value_destroy(val);
346	scf_iter_destroy(siter);
347	scf_iter_destroy(iiter);
348
349	(void) scf_handle_unbind(h);
350	scf_handle_destroy(h);
351}
352
353static void
354continue_delegates()
355{
356	ctidlist_t *next;
357	for (next = ctidlist; next != NULL; next = next->next)
358		(void) sigsend(P_CTID, next->ctid, SIGCONT);
359}
360
361static void
362stop_restarters()
363{
364	stop_startd();
365	stop_delegates();
366}
367
368static void
369continue_restarters()
370{
371	continue_startd();
372	continue_delegates();
373}
374
375/*
376 * Copy an array of strings into buf, separated by spaces.  Returns 0 on
377 * success.
378 */
379static int
380gather_args(char **args, char *buf, size_t buf_sz)
381{
382	if (strlcpy(buf, *args, buf_sz) >= buf_sz)
383		return (-1);
384
385	for (++args; *args != NULL; ++args) {
386		if (strlcat(buf, " ", buf_sz) >= buf_sz)
387			return (-1);
388		if (strlcat(buf, *args, buf_sz) >= buf_sz)
389			return (-1);
390	}
391
392	return (0);
393}
394
395/*
396 * Halt every zone on the system.  We are committed to doing a shutdown
397 * even if something goes wrong here. If something goes wrong, we just
398 * continue with the shutdown.  Return non-zero if we need to wait for zones to
399 * halt later on.
400 */
401static int
402halt_zones()
403{
404	pid_t pid;
405	zoneid_t *zones;
406	size_t nz = 0, old_nz;
407	int i;
408	char zname[ZONENAME_MAX];
409
410	/*
411	 * Get a list of zones. If the number of zones changes in between the
412	 * two zone_list calls, try again.
413	 */
414
415	for (;;) {
416		(void) zone_list(NULL, &nz);
417		if (nz == 1)
418			return (0);
419		old_nz = nz;
420		zones = calloc(sizeof (zoneid_t), nz);
421		if (zones == NULL) {
422			(void) fprintf(stderr,
423			    gettext("%s: Could not halt zones"
424			    " (out of memory).\n"), cmdname);
425			return (0);
426		}
427
428		(void) zone_list(zones, &nz);
429		if (old_nz == nz)
430			break;
431		free(zones);
432	}
433
434	if (nz == 2) {
435		(void) fprintf(stderr, gettext("%s: Halting 1 zone.\n"),
436		    cmdname);
437	} else {
438		(void) fprintf(stderr, gettext("%s: Halting %i zones.\n"),
439		    cmdname, nz - 1);
440	}
441
442	for (i = 0; i < nz; i++) {
443		if (zones[i] == GLOBAL_ZONEID)
444			continue;
445		if (getzonenamebyid(zones[i], zname, sizeof (zname)) < 0) {
446			/*
447			 * getzonenamebyid should only fail if we raced with
448			 * another process trying to shut down the zone.
449			 * We assume this happened and ignore the error.
450			 */
451			if (errno != EINVAL) {
452				(void) fprintf(stderr,
453				    gettext("%s: Unexpected error while "
454				    "looking up zone %ul: %s.\n"),
455				    cmdname, zones[i], strerror(errno));
456			}
457
458			continue;
459		}
460		pid = fork();
461		if (pid < 0) {
462			(void) fprintf(stderr,
463			    gettext("%s: Zone \"%s\" could not be"
464			    " halted (could not fork(): %s).\n"),
465			    cmdname, zname, strerror(errno));
466			continue;
467		}
468		if (pid == 0) {
469			(void) execl(ZONEADM_PROG, ZONEADM_PROG,
470			    "-z", zname, "halt", NULL);
471			(void) fprintf(stderr,
472			    gettext("%s: Zone \"%s\" could not be halted"
473			    " (cannot exec(" ZONEADM_PROG "): %s).\n"),
474			    cmdname, zname, strerror(errno));
475			exit(0);
476		}
477	}
478
479	return (1);
480}
481
482/*
483 * This function tries to wait for all non-global zones to go away.
484 * It will timeout if no progress is made for 5 seconds, or a total of
485 * 30 seconds elapses.
486 */
487
488static void
489check_zones_haltedness()
490{
491	int t = 0, t_prog = 0;
492	size_t nz = 0, last_nz;
493
494	do {
495		last_nz = nz;
496		(void) zone_list(NULL, &nz);
497		if (nz == 1)
498			return;
499
500		(void) sleep(1);
501
502		if (last_nz > nz)
503			t_prog = 0;
504
505		t++;
506		t_prog++;
507
508		if (t == 10) {
509			if (nz == 2) {
510				(void) fprintf(stderr,
511				    gettext("%s: Still waiting for 1 zone to "
512				    "halt. Will wait up to 20 seconds.\n"),
513				    cmdname);
514			} else {
515				(void) fprintf(stderr,
516				    gettext("%s: Still waiting for %i zones "
517				    "to halt. Will wait up to 20 seconds.\n"),
518				    cmdname, nz - 1);
519			}
520		}
521
522	} while ((t < 30) && (t_prog < 5));
523}
524
525
526/*
527 * Validate that this is a root disk or dataset
528 * Returns 0 if it is a root disk or dataset;
529 * returns 1 if it is a disk argument or dataset, but not valid or not root;
530 * returns -1 if it is not a valid argument or a disk argument.
531 */
532static int
533validate_disk(char *arg, char *mountpoint)
534{
535	static char root_dev_path[] = "/dev/dsk";
536	char kernpath[MAXPATHLEN];
537	struct stat buf;
538	struct stat64 statbuf;
539	int rc = 0;
540
541	if (strlen(arg) > MAXPATHLEN) {
542		(void) fprintf(stderr,
543		    gettext("%s: argument is too long\n"), cmdname);
544		return (-1);
545	}
546
547	bcopy(FASTBOOT_MOUNTPOINT, mountpoint, sizeof (FASTBOOT_MOUNTPOINT));
548
549	/*
550	 * Do a force umount just in case some other filesystem has
551	 * been mounted there.
552	 */
553	(void) umount2(mountpoint, MS_FORCE);
554
555	/* Create the directory if it doesn't already exist */
556	if (lstat(mountpoint, &buf) != 0) {
557		if (mkdirp(mountpoint, 0755) != 0) {
558			(void) fprintf(stderr,
559			    gettext("failed to create mountpoint %s\n"),
560			    mountpoint);
561			return (-1);
562		}
563	}
564
565	if (strncmp(arg, root_dev_path, strlen(root_dev_path)) == 0) {
566		/* ufs root disk argument */
567		rc = validate_ufs_disk(arg, mountpoint);
568	} else {
569		/* zfs root pool argument */
570		rc = validate_zfs_pool(arg, mountpoint);
571	}
572
573	if (rc != 0)
574		return (rc);
575
576	(void) snprintf(kernpath, MAXPATHLEN, "%s/platform/i86pc/kernel/unix",
577	    mountpoint);
578
579	if (stat64(kernpath, &statbuf) != 0) {
580		(void) fprintf(stderr,
581		    gettext("%s: %s is not a root disk or dataset\n"),
582		    cmdname, arg);
583		return (1);
584	}
585
586	return (0);
587}
588
589
590static int
591validate_ufs_disk(char *arg, char *mountpoint)
592{
593	char mntopts[MNT_LINE_MAX] = { '\0' };
594
595	/* perform the mount */
596	if (mount(arg, mountpoint, MS_DATA|MS_OPTIONSTR,
597	    MNTTYPE_UFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) {
598		perror(cmdname);
599		(void) fprintf(stderr,
600		    gettext("%s: failed to mount %s\n"), cmdname, arg);
601		return (-1);
602	}
603
604	return (0);
605}
606
607static int
608validate_zfs_pool(char *arg, char *mountpoint)
609{
610	zfs_handle_t *zhp = NULL;
611	char mntopts[MNT_LINE_MAX] = { '\0' };
612	int rc = 0;
613
614	if ((g_zfs = libzfs_init()) == NULL) {
615		(void) fprintf(stderr, gettext("internal error: failed to "
616		    "initialize ZFS library\n"));
617		return (-1);
618	}
619
620	/* Try to open the dataset */
621	if ((zhp = zfs_open(g_zfs, arg,
622	    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_DATASET)) == NULL)
623		return (-1);
624
625	/* perform the mount */
626	if (mount(zfs_get_name(zhp), mountpoint, MS_DATA|MS_OPTIONSTR,
627	    MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) {
628		perror(cmdname);
629		(void) fprintf(stderr,
630		    gettext("%s: failed to mount %s\n"), cmdname, arg);
631		rc = -1;
632	}
633
634validate_zfs_err_out:
635	if (zhp != NULL)
636		zfs_close(zhp);
637
638	libzfs_fini(g_zfs);
639	return (rc);
640}
641
642/*
643 * Return 0 if not zfs, or is zfs and have successfully constructed the
644 * boot argument; returns non-zero otherwise.
645 * At successful completion fpth contains pointer where mount point ends.
646 * NOTE: arg is supposed to be the resolved path
647 */
648static int
649get_zfs_bootfs_arg(const char *arg, const char ** fpth, int *is_zfs,
650		char *bootfs_arg)
651{
652	zfs_handle_t *zhp = NULL;
653	zpool_handle_t *zpoolp = NULL;
654	FILE *mtabp = NULL;
655	struct mnttab mnt;
656	char *poolname = NULL;
657	char physpath[MAXNAMELEN];
658	char mntsp[ZPOOL_MAXNAMELEN];
659	char bootfs[ZPOOL_MAXNAMELEN];
660	int rc = 0;
661	size_t mntlen = 0;
662	size_t msz;
663
664	*fpth = arg;
665	*is_zfs = 0;
666
667	bzero(physpath, sizeof (physpath));
668	bzero(bootfs, sizeof (bootfs));
669
670	if ((mtabp = fopen(MNTTAB, "r")) == NULL) {
671		return (-1);
672	}
673
674	while (getmntent(mtabp, &mnt) == 0) {
675		if (strstr(arg, mnt.mnt_mountp) == arg &&
676		    (msz = strlen(mnt.mnt_mountp)) > mntlen) {
677			mntlen = msz;
678			*is_zfs = strcmp(MNTTYPE_ZFS, mnt.mnt_fstype) == 0;
679			(void) strlcpy(mntsp, mnt.mnt_special, sizeof (mntsp));
680		}
681	}
682
683	(void) fclose(mtabp);
684
685	if (mntlen > 1)
686		*fpth += mntlen;
687
688	if (!*is_zfs)
689		return (0);
690
691	if ((g_zfs = libzfs_init()) == NULL)
692		return (-1);
693
694	/* Try to open the dataset */
695	if ((zhp = zfs_open(g_zfs, mntsp,
696	    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_DATASET)) == NULL) {
697		(void) fprintf(stderr, gettext("cannot open %s\n"), mntsp);
698		rc = -1;
699		goto validate_zfs_err_out;
700	}
701
702	(void) strlcpy(bootfs, mntsp, sizeof (bootfs));
703
704	if ((poolname = strtok(mntsp, "/")) == NULL) {
705		rc = -1;
706		goto validate_zfs_err_out;
707	}
708
709	if ((zpoolp = zpool_open(g_zfs, poolname)) == NULL) {
710		(void) fprintf(stderr, gettext("cannot open %s\n"), poolname);
711		rc = -1;
712		goto validate_zfs_err_out;
713	}
714
715	if (zpool_get_physpath(zpoolp, physpath) != 0) {
716		(void) fprintf(stderr, gettext("cannot find phys_path\n"));
717		rc = -1;
718		goto validate_zfs_err_out;
719	}
720
721	if (zpool_set_prop(zpoolp, "bootfs", bootfs) != 0) {
722		(void) fprintf(stderr, gettext("cannot set bootfs to %s\n"),
723		    bootfs);
724		rc = -1;
725		goto validate_zfs_err_out;
726	}
727
728	(void) snprintf(bootfs_arg, BOOTARGS_MAX,
729	    "-B zfs-bootfs=%s,bootpath=\"%s\"", bootfs, physpath);
730
731validate_zfs_err_out:
732	if (zhp != NULL)
733		zfs_close(zhp);
734
735	if (zpoolp != NULL)
736		zpool_close(zpoolp);
737
738	libzfs_fini(g_zfs);
739	return (rc);
740}
741
742/*
743 * Validate that the file exists, and is an ELF file.
744 * Returns 0 on success, -1 on failure.
745 */
746static int
747validate_unix(char *arg, int *mplen, int *is_zfs, char *bootfs_arg,
748    int *failsafe)
749{
750	const char *location;
751	int class, format;
752	unsigned char ident[EI_NIDENT];
753	char physpath[MAXPATHLEN];
754	int elffd = -1;
755	size_t	sz;
756
757	if ((sz = resolvepath(arg, physpath, sizeof (physpath) - 1)) ==
758	    (size_t)-1) {
759		(void) fprintf(stderr,
760		    gettext("cannot resolve path for %s: %s\n"),
761		    arg, strerror(errno));
762		return (-1);
763	}
764	(void) strlcpy(arg, physpath, sz + 1);
765
766	if (strlen(arg) > MAXPATHLEN) {
767		(void) fprintf(stderr,
768		    gettext("%s: new kernel name is too long\n"), cmdname);
769		return (-1);
770	}
771
772	if (strncmp(basename(arg), "unix", 4) != 0) {
773		(void) fprintf(stderr,
774		    gettext("%s: %s: kernel name must be unix\n"),
775		    cmdname, arg);
776		return (-1);
777	}
778
779	if (get_zfs_bootfs_arg(arg, &location, is_zfs, bootfs_arg) != 0)
780		goto err_out;
781
782	*mplen = location - arg;
783
784	if ((strstr(location, "/boot/platform")) == location)
785		*failsafe = 1;
786	else if ((strstr(location, "/platform")) == location)
787		*failsafe = 0;
788	else	{
789		(void) fprintf(stderr,
790		    gettext("%s: %s: no /boot/platform or /platform in"
791		    " file name\n"), cmdname, arg);
792			goto err_out;
793	}
794
795	if ((elffd = open64(arg, O_RDONLY)) < 0 ||
796	    (pread64(elffd, ident, EI_NIDENT, 0) != EI_NIDENT)) {
797		(void) fprintf(stderr, "%s: %s: %s\n",
798		    cmdname, arg, strerror(errno));
799		goto err_out;
800	}
801
802	class = ident[EI_CLASS];
803
804	if ((class != ELFCLASS32 && class != ELFCLASS64) ||
805	    ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 ||
806	    ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3) {
807		(void) fprintf(stderr,
808		    gettext("%s: %s: not a valid ELF file\n"),
809		    cmdname, arg);
810		goto err_out;
811	}
812
813	format = ident[EI_DATA];
814
815	if (format != CUR_ELFDATA) {
816		(void) fprintf(stderr, gettext("%s: %s: invalid data format\n"),
817		    cmdname, arg);
818		goto err_out;
819	}
820
821	return (0);
822
823err_out:
824	if (elffd >= 0) {
825		(void) close(elffd);
826		elffd = -1;
827	}
828	return (-1);
829}
830
831#ifndef	__i386
832/* ARGSUSED */
833#endif	/* __i386 */
834static int
835is_fastboot_default(uid_t uid)
836{
837#if defined(__i386)
838	int		ret;
839	struct stat	st;
840	static const char	fastboot_default[] = "/etc/fastreboot";
841
842	ret = (lstat(fastboot_default, &st) == 0 &&
843	    S_ISREG(st.st_mode) &&
844	    (st.st_mode & S_IRUSR) != 0 &&
845	    uid == st.st_uid);
846
847	return (ret);
848#else
849	return (0);
850#endif	/* __i386 */
851}
852
853static int
854fastboot_bename(const char *bename, char *mountpoint, size_t mpsz)
855{
856	int rc;
857	char cmdbuf[MAXPATHLEN];
858
859	(void) snprintf(cmdbuf, sizeof (cmdbuf),
860	    "/usr/sbin/luumount %s > /dev/null 2>&1", bename);
861	(void) system(cmdbuf);
862
863	(void) snprintf(cmdbuf, sizeof (cmdbuf),
864	    "/usr/sbin/lumount %s %s > /dev/null 2>&1",
865	    bename, FASTBOOT_MOUNTPOINT);
866	if ((rc = system(cmdbuf)) != 0)
867		(void) fprintf(stderr, gettext("%s: cannot mount BE %s\n"),
868		    cmdname, bename);
869	else
870		(void) strlcpy(mountpoint, FASTBOOT_MOUNTPOINT, mpsz);
871
872	return (rc);
873}
874
875/*
876 * Returns 0 on successful parsing of the arguments;
877 * retuens non-zero on failure.
878 */
879static int
880parse_fastboot_args(char *bootargs_buf, int *is_dryrun, const char *bename,
881    int *failsafe)
882{
883	char mountpoint[MAXPATHLEN];
884	char bootargs_saved[BOOTARGS_MAX];
885	char bootargs_scratch[BOOTARGS_MAX];
886	char bootfs_arg[BOOTARGS_MAX];
887	char unixfile[BOOTARGS_MAX];
888	char *head, *newarg;
889	int buflen;		/* length of the bootargs_buf */
890	int mplen;		/* length of the mount point */
891	int rootlen = 0;	/* length of the root argument */
892	int unixlen = 0;	/* length of the unix argument */
893	int off = 0;		/* offset into the new boot argument */
894	int is_zfs = 0;
895	int rc = 0;
896
897	bzero(mountpoint, sizeof (mountpoint));
898
899	/*
900	 * If argc is not 0, buflen is length of the argument being passed in;
901	 * else it is 0 as bootargs_buf has been initialized to all 0's.
902	 */
903	buflen = strlen(bootargs_buf);
904
905	/* Save a copy of the original argument */
906	bcopy(bootargs_buf, bootargs_saved, buflen);
907	bzero(&bootargs_saved[buflen], sizeof (bootargs_saved) - buflen);
908
909	/* Save another copy to be used by strtok */
910	bcopy(bootargs_buf, bootargs_scratch, buflen);
911	bzero(&bootargs_scratch[buflen], sizeof (bootargs_scratch) - buflen);
912	head = &bootargs_scratch[0];
913
914	/* Zero out the boot argument buffer as we will reconstruct it */
915	bzero(bootargs_buf, BOOTARGS_MAX);
916	bzero(bootfs_arg, BOOTARGS_MAX);
917	bzero(unixfile, sizeof (unixfile));
918
919	/* Get the first argument */
920	newarg = strtok(bootargs_scratch, " ");
921
922	/*
923	 * If this is a dry run request, verify that the drivers can handle
924	 * fast reboot.
925	 */
926	if (newarg && strncasecmp(newarg, "dryrun", strlen("dryrun")) == 0) {
927		*is_dryrun = 1;
928		(void) system("/usr/sbin/devfsadm");
929	}
930
931	/*
932	 * Always perform a dry run to identify all the drivers that
933	 * need to implement devo_reset().
934	 */
935	if (uadmin(A_SHUTDOWN, AD_FASTREBOOT_DRYRUN,
936	    (uintptr_t)bootargs_saved) != 0) {
937		(void) fprintf(stderr, gettext("%s: not all drivers "
938		    "have implemented quiesce(9E)\n"), cmdname);
939	} else if (*is_dryrun) {
940		(void) fprintf(stderr, gettext("%s: all drivers have "
941		    "implemented quiesce(9E)\n"), cmdname);
942	}
943
944	/*
945	 * Return if it is a true dry run.
946	 */
947	if (*is_dryrun)
948		return (rc);
949
950	if (bename && (rc = fastboot_bename(bename, mountpoint,
951	    sizeof (mountpoint))) != 0)
952		return (rc);
953
954	/*
955	 * If BE is not specified, look for disk argument to construct
956	 * mountpoint; if BE has been specified, mountpoint has already been
957	 * constructed.
958	 */
959	if (newarg && newarg[0] != '-' && !bename) {
960		int tmprc;
961
962		if ((tmprc = validate_disk(newarg, mountpoint)) == 0) {
963			/*
964			 * The first argument is a valid root argument.
965			 * Get the next argument.
966			 */
967			newarg = strtok(NULL, " ");
968			rootlen = (newarg) ? (newarg - head) : buflen;
969			(void) strlcpy(fastboot_mounted, mountpoint,
970			    sizeof (fastboot_mounted));
971
972		} else if (tmprc == -1) {
973			/*
974			 * Not a disk argument.  Use / as default root.
975			 */
976			bcopy("/", mountpoint, 1);
977			bzero(&mountpoint[1], sizeof (mountpoint) - 1);
978		} else {
979			/*
980			 * Disk argument, but not valid or not root.
981			 * Return failure.
982			 */
983			return (EINVAL);
984		}
985	}
986
987	/*
988	 * Make mountpoint the first part of unixfile.
989	 * If there is not disk argument, and BE has not been specified,
990	 * mountpoint could be empty.
991	 */
992	mplen = strlen(mountpoint);
993	bcopy(mountpoint, unixfile, mplen);
994
995	/*
996	 * Look for unix argument
997	 */
998	if (newarg && newarg[0] != '-') {
999		bcopy(newarg, &unixfile[mplen], strlen(newarg));
1000		newarg = strtok(NULL, " ");
1001		rootlen = (newarg) ? (newarg - head) : buflen;
1002	} else if (mplen != 0) {
1003		/*
1004		 * No unix argument, but mountpoint is not empty, use
1005		 * /platform/i86pc/$ISADIR/kernel/unix as default.
1006		 */
1007		char isa[20];
1008
1009		if (sysinfo(SI_ARCHITECTURE_64, isa, sizeof (isa)) != -1)
1010			(void) snprintf(&unixfile[mplen],
1011			    sizeof (unixfile) - mplen,
1012			    "/platform/i86pc/kernel/%s/unix", isa);
1013		else if (sysinfo(SI_ARCHITECTURE_32, isa, sizeof (isa)) != -1) {
1014			(void) snprintf(&unixfile[mplen],
1015			    sizeof (unixfile) - mplen,
1016			    "/platform/i86pc/kernel/unix");
1017		} else {
1018			(void) fprintf(stderr,
1019			    gettext("%s: unknown architecture"), cmdname);
1020			return (EINVAL);
1021		}
1022	}
1023
1024	/*
1025	 * We now have the complete unix argument.  Verify that it exists and
1026	 * is an ELF file.  Split the argument up into mountpoint and unix
1027	 * portions again.  This is necessary to handle cases where mountpoint
1028	 * is specified on the command line as part of the unix argument,
1029	 * such as this:
1030	 *	# reboot -f /.alt/platform/i86pc/kernel/amd64/unix
1031	 */
1032	unixlen = strlen(unixfile);
1033	if (unixlen > 0) {
1034		if (validate_unix(unixfile, &mplen, &is_zfs,
1035		    bootfs_arg, failsafe) != 0) {
1036			/* Not a valid unix file */
1037			return (EINVAL);
1038		} else {
1039			/*
1040			 * Construct boot argument.
1041			 */
1042			unixlen = strlen(unixfile);
1043			bcopy(unixfile, bootargs_buf, mplen);
1044			(void) strcat(bootargs_buf, " ");
1045			bcopy(&unixfile[mplen], &bootargs_buf[mplen + 1],
1046			    unixlen - mplen);
1047			(void) strcat(bootargs_buf, " ");
1048			off += unixlen + 2;
1049		}
1050	} else {
1051		/* Check to see if root is zfs */
1052		const char	*dp;
1053		(void) get_zfs_bootfs_arg("/", &dp, &is_zfs, bootfs_arg);
1054	}
1055
1056	if (is_zfs && (buflen != 0 || bename != NULL))	{
1057		/* LINTED E_SEC_SPRINTF_UNBOUNDED_COPY */
1058		off += sprintf(bootargs_buf + off, "%s ", bootfs_arg);
1059	}
1060
1061	/*
1062	 * Copy the rest of the arguments
1063	 */
1064	bcopy(&bootargs_saved[rootlen], &bootargs_buf[off], buflen - rootlen);
1065
1066	return (rc);
1067}
1068
1069int
1070main(int argc, char *argv[])
1071{
1072	char *ttyn = ttyname(STDERR_FILENO);
1073
1074	uid_t	euid;
1075	int qflag = 0, needlog = 1, nosync = 0;
1076	int fast_reboot = 0;
1077	uintptr_t mdep = NULL;
1078	int cmd, fcn, c, aval, r;
1079	const char *usage;
1080	zoneid_t zoneid = getzoneid();
1081	int need_check_zones = 0;
1082	char bootargs_buf[BOOTARGS_MAX];
1083	int failsafe = 0;
1084	char *bename = NULL;
1085
1086	const char * const resetting = "/etc/svc/volatile/resetting";
1087
1088	(void) setlocale(LC_ALL, "");
1089	(void) textdomain(TEXT_DOMAIN);
1090
1091	cmdname = basename(argv[0]);
1092
1093	if (strcmp(cmdname, "halt") == 0) {
1094		(void) audit_halt_setup(argc, argv);
1095		usage = gettext("usage: %s [ -dlnqy ]\n");
1096		cmd = A_SHUTDOWN;
1097		fcn = AD_HALT;
1098	} else if (strcmp(cmdname, "poweroff") == 0) {
1099		(void) audit_halt_setup(argc, argv);
1100		usage = gettext("usage: %s [ -dlnqy ]\n");
1101		cmd = A_SHUTDOWN;
1102		fcn = AD_POWEROFF;
1103	} else if (strcmp(cmdname, "reboot") == 0) {
1104		(void) audit_reboot_setup();
1105#if defined(__i386)
1106		usage = gettext("usage: %s [ -dlnqfe: ] [ boot args ]\n");
1107#else
1108		usage = gettext("usage: %s [ -dlnq ] [ boot args ]\n");
1109#endif
1110		cmd = A_SHUTDOWN;
1111		fcn = AD_BOOT;
1112	} else {
1113		(void) fprintf(stderr,
1114		    gettext("%s: not installed properly\n"), cmdname);
1115		return (1);
1116	}
1117
1118	while ((c = getopt(argc, argv, "dlnqyfe:")) != EOF) {
1119		switch (c) {
1120		case 'd':
1121			if (zoneid == GLOBAL_ZONEID)
1122				cmd = A_DUMP;
1123			else {
1124				(void) fprintf(stderr,
1125				    gettext("%s: -d only valid from global"
1126				    " zone\n"), cmdname);
1127				return (1);
1128			}
1129			break;
1130		case 'l':
1131			needlog = 0;
1132			break;
1133		case 'n':
1134			nosync = 1;
1135			break;
1136		case 'q':
1137			qflag = 1;
1138			break;
1139		case 'y':
1140			ttyn = NULL;
1141			break;
1142#if defined(__i386)
1143		case 'f':
1144			fast_reboot = 1;
1145			break;
1146		case 'e':
1147			bename = optarg;
1148			break;
1149#endif
1150		default:
1151			/*
1152			 * TRANSLATION_NOTE
1153			 * Don't translate the words "halt" or "reboot"
1154			 */
1155			(void) fprintf(stderr, usage, cmdname);
1156			return (1);
1157		}
1158	}
1159
1160	argc -= optind;
1161	argv += optind;
1162
1163	if (argc != 0) {
1164		if (fcn != AD_BOOT) {
1165			(void) fprintf(stderr, usage, cmdname);
1166			return (1);
1167		}
1168
1169		/* Gather the arguments into bootargs_buf. */
1170		if (gather_args(argv, bootargs_buf, sizeof (bootargs_buf)) !=
1171		    0) {
1172			(void) fprintf(stderr,
1173			    gettext("%s: Boot arguments too long.\n"), cmdname);
1174			return (1);
1175		}
1176
1177		mdep = (uintptr_t)bootargs_buf;
1178	} else {
1179		/*
1180		 * Initialize it to 0 in case of fastboot, the buffer
1181		 * will be used.
1182		 */
1183		bzero(bootargs_buf, sizeof (bootargs_buf));
1184	}
1185
1186	if ((euid = geteuid()) != 0) {
1187		(void) fprintf(stderr,
1188		    gettext("%s: permission denied\n"), cmdname);
1189		goto fail;
1190	}
1191
1192	/*
1193	 * Check whether fast  reboot is the default operating mode
1194	 */
1195	if (!fast_reboot)
1196		fast_reboot = is_fastboot_default(euid);
1197
1198	if (bename && !fast_reboot)	{
1199		(void) fprintf(stderr, gettext("%s: -e only valid with -f\n"),
1200		    cmdname);
1201		return (EINVAL);
1202	}
1203
1204
1205	/*
1206	 * If fast reboot, do some sanity check on the argument
1207	 */
1208	if (fast_reboot) {
1209		int rc;
1210		int is_dryrun = 0;
1211
1212		if (zoneid != GLOBAL_ZONEID)	{
1213			(void) fprintf(stderr,
1214			    gettext("%s: fast reboot only valid from global"
1215			    " zone\n"), cmdname);
1216			return (EINVAL);
1217		}
1218
1219		rc = parse_fastboot_args(bootargs_buf, &is_dryrun,
1220		    bename, &failsafe);
1221
1222		/*
1223		 * If dry run, or if arguments are invalid, return.
1224		 */
1225		if (is_dryrun)
1226			return (rc);
1227		else if (rc != 0)
1228			goto fail;
1229
1230		/*
1231		 * For all the other errors, we continue on in case user
1232		 * user want to force fast reboot.
1233		 */
1234		if (strlen(bootargs_buf) != 0)
1235			mdep = (uintptr_t)bootargs_buf;
1236	}
1237
1238#if 0	/* For debugging */
1239	if (mdep != NULL)
1240		(void) fprintf(stderr, "mdep = %s\n", (char *)mdep);
1241#endif
1242
1243	if (fcn != AD_BOOT && ttyn != NULL &&
1244	    strncmp(ttyn, "/dev/term/", strlen("/dev/term/")) == 0) {
1245		/*
1246		 * TRANSLATION_NOTE
1247		 * Don't translate ``halt -y''
1248		 */
1249		(void) fprintf(stderr,
1250		    gettext("%s: dangerous on a dialup;"), cmdname);
1251		(void) fprintf(stderr,
1252		    gettext("use ``%s -y'' if you are really sure\n"), cmdname);
1253		goto fail;
1254	}
1255
1256	if (needlog) {
1257		char *user = getlogin();
1258		struct passwd *pw;
1259		char *tty;
1260
1261		openlog(cmdname, 0, LOG_AUTH);
1262		if (user == NULL && (pw = getpwuid(getuid())) != NULL)
1263			user = pw->pw_name;
1264		if (user == NULL)
1265			user = "root";
1266
1267		tty = ttyname(1);
1268
1269		if (tty == NULL)
1270			syslog(LOG_CRIT, "initiated by %s", user);
1271		else
1272			syslog(LOG_CRIT, "initiated by %s on %s", user, tty);
1273	}
1274
1275	/*
1276	 * We must assume success and log it before auditd is terminated.
1277	 */
1278	if (fcn == AD_BOOT)
1279		aval = audit_reboot_success();
1280	else
1281		aval = audit_halt_success();
1282
1283	if (aval == -1) {
1284		(void) fprintf(stderr,
1285		    gettext("%s: can't turn off auditd\n"), cmdname);
1286		if (needlog)
1287			(void) sleep(5); /* Give syslogd time to record this */
1288	}
1289
1290	(void) signal(SIGHUP, SIG_IGN);	/* for remote connections */
1291
1292	/*
1293	 * We start to fork a bunch of zoneadms to halt any active zones.
1294	 * This will proceed with halt in parallel until we call
1295	 * check_zone_haltedness later on.
1296	 */
1297	if (zoneid == GLOBAL_ZONEID && cmd != A_DUMP) {
1298		need_check_zones = halt_zones();
1299	}
1300
1301
1302	/* sync boot archive in the global zone */
1303	if (zoneid == GLOBAL_ZONEID && !nosync) {
1304		if (fast_reboot)
1305			(void) system("/sbin/bootadm -a update_all fastboot");
1306		else
1307			(void) system("/sbin/bootadm -a update_all");
1308	}
1309
1310	/*
1311	 * If we're not forcing a crash dump, mark the system as quiescing for
1312	 * smf(5)'s benefit, and idle the init process.
1313	 */
1314	if (cmd != A_DUMP) {
1315		if (direct_init(PCDSTOP) == -1) {
1316			/*
1317			 * TRANSLATION_NOTE
1318			 * Don't translate the word "init"
1319			 */
1320			(void) fprintf(stderr,
1321			    gettext("%s: can't idle init\n"), cmdname);
1322			goto fail;
1323		}
1324
1325		if (creat(resetting, 0755) == -1)
1326			(void) fprintf(stderr,
1327			    gettext("%s: could not create %s.\n"),
1328			    cmdname, resetting);
1329
1330		/*
1331		 * Stop all restarters so they do not try to restart services
1332		 * that are terminated.
1333		 */
1334		stop_restarters();
1335
1336		/*
1337		 * Wait a little while for zones to shutdown.
1338		 */
1339		if (need_check_zones) {
1340			check_zones_haltedness();
1341
1342			(void) fprintf(stderr,
1343			    gettext("%s: Completing system halt.\n"),
1344			    cmdname);
1345		}
1346	}
1347
1348	/*
1349	 * Make sure we don't get stopped by a jobcontrol shell
1350	 * once we start killing everybody.
1351	 */
1352	(void) signal(SIGTSTP, SIG_IGN);
1353	(void) signal(SIGTTIN, SIG_IGN);
1354	(void) signal(SIGTTOU, SIG_IGN);
1355	(void) signal(SIGTERM, SIG_IGN);
1356
1357	/*
1358	 * If we're not forcing a crash dump, give everyone 5 seconds to
1359	 * handle a SIGTERM and clean up properly.
1360	 */
1361	if (cmd != A_DUMP) {
1362		(void) kill(-1, SIGTERM);
1363		(void) sleep(5);
1364	}
1365
1366	if (!qflag && !nosync) {
1367		struct utmpx wtmpx;
1368
1369		bzero(&wtmpx, sizeof (struct utmpx));
1370		(void) strcpy(wtmpx.ut_line, "~");
1371		(void) time(&wtmpx.ut_tv.tv_sec);
1372
1373		if (cmd == A_DUMP)
1374			(void) strcpy(wtmpx.ut_name, "crash dump");
1375		else
1376			(void) strcpy(wtmpx.ut_name, "shutdown");
1377
1378		(void) updwtmpx(WTMPX_FILE, &wtmpx);
1379		sync();
1380	}
1381
1382	if (cmd == A_DUMP && nosync != 0)
1383		(void) uadmin(A_DUMP, AD_NOSYNC, NULL);
1384
1385	if (fast_reboot) {
1386		if (failsafe)
1387			(void) fprintf(stderr, "Fast reboot - failsafe.\n");
1388		else
1389			(void) fprintf(stderr, "Fast reboot.\n");
1390
1391		fcn = AD_FASTREBOOT;
1392	}
1393
1394	if (uadmin(cmd, fcn, mdep) == -1)
1395		(void) fprintf(stderr, "%s: uadmin failed: %s\n",
1396		    cmdname, strerror(errno));
1397	else
1398		(void) fprintf(stderr, "%s: uadmin unexpectedly returned 0\n",
1399		    cmdname);
1400
1401	do {
1402		r = remove(resetting);
1403	} while (r != 0 && errno == EINTR);
1404
1405	if (r != 0 && errno != ENOENT)
1406		(void) fprintf(stderr, gettext("%s: could not remove %s.\n"),
1407		    cmdname, resetting);
1408
1409	if (direct_init(PCRUN) == -1) {
1410		/*
1411		 * TRANSLATION_NOTE
1412		 * Don't translate the word "init"
1413		 */
1414		(void) fprintf(stderr,
1415		    gettext("%s: can't resume init\n"), cmdname);
1416	}
1417
1418	continue_restarters();
1419
1420	if (get_initpid() != -1)
1421		/* tell init to restate current level */
1422		(void) kill(get_initpid(), SIGHUP);
1423
1424fail:
1425	if (fcn == AD_BOOT)
1426		(void) audit_reboot_fail();
1427	else
1428		(void) audit_halt_fail();
1429
1430	if (fast_reboot) {
1431		if (bename) {
1432			char cmdbuf[MAXPATHLEN];
1433
1434			(void) snprintf(cmdbuf, sizeof (cmdbuf),
1435			    "/usr/sbin/luumount %s > /dev/null 2>&1", bename);
1436			(void) system(cmdbuf);
1437
1438		} else if (strlen(fastboot_mounted) != 0) {
1439			(void) umount(fastboot_mounted);
1440		}
1441	}
1442
1443	return (1);
1444}
1445