xref: /illumos-gate/usr/src/cmd/acctadm/utils.c (revision 074e084f68dd0b08686612bec695a0cfe249da6d)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5d362b749Svk  * Common Development and Distribution License (the "License").
6d362b749Svk  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*074e084fSml  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23d362b749Svk  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate 
28*074e084fSml #include <assert.h>
29*074e084fSml #include <sys/types.h>
30*074e084fSml #include <sys/acctctl.h>
317c478bd9Sstevel@tonic-gate #include <sys/param.h>
32*074e084fSml #include <sys/stat.h>
337c478bd9Sstevel@tonic-gate #include <libintl.h>
347c478bd9Sstevel@tonic-gate #include <string.h>
357c478bd9Sstevel@tonic-gate #include <stdlib.h>
367c478bd9Sstevel@tonic-gate #include <stdarg.h>
377c478bd9Sstevel@tonic-gate #include <stdio.h>
38*074e084fSml #include <strings.h>
39*074e084fSml #include <unistd.h>
407c478bd9Sstevel@tonic-gate #include <errno.h>
41*074e084fSml #include <exacct.h>
42*074e084fSml #include <fcntl.h>
43*074e084fSml #include <priv.h>
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate #include "utils.h"
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate static char PNAME_FMT[] = "%s: ";
487c478bd9Sstevel@tonic-gate static char ERRNO_FMT[] = ": %s\n";
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate static char *pname;
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate /*PRINTFLIKE1*/
537c478bd9Sstevel@tonic-gate void
54d362b749Svk warn(const char *format, ...)
557c478bd9Sstevel@tonic-gate {
567c478bd9Sstevel@tonic-gate 	int err = errno;
577c478bd9Sstevel@tonic-gate 	va_list alist;
587c478bd9Sstevel@tonic-gate 	if (pname != NULL)
597c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(PNAME_FMT), pname);
607c478bd9Sstevel@tonic-gate 	va_start(alist, format);
617c478bd9Sstevel@tonic-gate 	(void) vfprintf(stderr, format, alist);
627c478bd9Sstevel@tonic-gate 	va_end(alist);
637c478bd9Sstevel@tonic-gate 	if (strchr(format, '\n') == NULL)
647c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERRNO_FMT), strerror(err));
657c478bd9Sstevel@tonic-gate }
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate /*PRINTFLIKE1*/
687c478bd9Sstevel@tonic-gate void
697c478bd9Sstevel@tonic-gate die(char *format, ...)
707c478bd9Sstevel@tonic-gate {
717c478bd9Sstevel@tonic-gate 	int err = errno;
727c478bd9Sstevel@tonic-gate 	va_list alist;
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate 	if (pname != NULL)
757c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(PNAME_FMT), pname);
767c478bd9Sstevel@tonic-gate 	va_start(alist, format);
777c478bd9Sstevel@tonic-gate 	(void) vfprintf(stderr, format, alist);
787c478bd9Sstevel@tonic-gate 	va_end(alist);
797c478bd9Sstevel@tonic-gate 	if (strchr(format, '\n') == NULL)
807c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERRNO_FMT), strerror(err));
817c478bd9Sstevel@tonic-gate 	exit(E_ERROR);
827c478bd9Sstevel@tonic-gate }
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate char *
857c478bd9Sstevel@tonic-gate setprogname(char *arg0)
867c478bd9Sstevel@tonic-gate {
877c478bd9Sstevel@tonic-gate 	char *p = strrchr(arg0, '/');
887c478bd9Sstevel@tonic-gate 
897c478bd9Sstevel@tonic-gate 	if (p == NULL)
907c478bd9Sstevel@tonic-gate 		p = arg0;
917c478bd9Sstevel@tonic-gate 	else
927c478bd9Sstevel@tonic-gate 		p++;
937c478bd9Sstevel@tonic-gate 	pname = p;
947c478bd9Sstevel@tonic-gate 	return (pname);
957c478bd9Sstevel@tonic-gate }
967c478bd9Sstevel@tonic-gate 
97*074e084fSml /*
98*074e084fSml  * Return the localized name of an accounting type.
99*074e084fSml  */
100*074e084fSml const char *
101*074e084fSml ac_type_name(int type)
102*074e084fSml {
103*074e084fSml 	switch (type) {
104*074e084fSml 	case AC_PROC:
105*074e084fSml 		return (gettext("process"));
106*074e084fSml 	case AC_FLOW:
107*074e084fSml 		return (gettext("flow"));
108*074e084fSml 	case AC_TASK:
109*074e084fSml 		return (gettext("task"));
110*074e084fSml 	default:
111*074e084fSml 		die(gettext("invalid type %d\n"), type);
112*074e084fSml 	}
113*074e084fSml 	/* NOTREACHED */
114*074e084fSml 	return (NULL);
115*074e084fSml }
116*074e084fSml 
117*074e084fSml /*
118*074e084fSml  * Open an accounting file.  The filename specified must be an absolute
119*074e084fSml  * pathname and the existing contents of the file (if any) must be of the
120*074e084fSml  * requested type.  Needs euid 0 to open the root-owned accounting file.
121*074e084fSml  * file_dac_write is required to create a new file in a directory not owned
122*074e084fSml  * by root (/var/adm/exacct is owned by 'adm').  Assumes sys_acct privilege is
123*074e084fSml  * already asserted by caller.
124*074e084fSml  */
1257c478bd9Sstevel@tonic-gate int
126*074e084fSml open_exacct_file(const char *file, int type)
1277c478bd9Sstevel@tonic-gate {
128*074e084fSml 	int rc;
129*074e084fSml 	int err;
130*074e084fSml 
131*074e084fSml 	if (file[0] != '/') {
132*074e084fSml 		warn(gettext("%s is not an absolute pathname\n"), file);
133*074e084fSml 		return (-1);
1347c478bd9Sstevel@tonic-gate 	}
135*074e084fSml 	if (!verify_exacct_file(file, type)) {
136*074e084fSml 		warn(gettext("%s is not a %s accounting file\n"), file,
137*074e084fSml 		    ac_type_name(type));
138*074e084fSml 		return (-1);
139*074e084fSml 	}
140*074e084fSml 	if (seteuid(0) == -1 || setegid(0) == -1) {
141*074e084fSml 		warn(gettext("seteuid()/setegid() failed"));
142*074e084fSml 		return (-1);
143*074e084fSml 	}
144*074e084fSml 	assert(priv_ineffect(PRIV_SYS_ACCT));
145*074e084fSml 	(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_FILE_DAC_WRITE, NULL);
146*074e084fSml 	rc = acctctl(type | AC_FILE_SET, (void *) file, strlen(file) + 1);
147*074e084fSml 	if (rc == -1 && (err = errno) == EBUSY) {
148*074e084fSml 		char name[MAXPATHLEN];
149*074e084fSml 		struct stat cur;
150*074e084fSml 		struct stat new;
151*074e084fSml 
152*074e084fSml 		/*
153*074e084fSml 		 * The file is already open as an accounting file somewhere.
154*074e084fSml 		 * If the file we're trying to open is the same as we have
155*074e084fSml 		 * currently open then we're ok.
156*074e084fSml 		 */
157*074e084fSml 		if (acctctl(type | AC_FILE_GET, name, sizeof (name)) == 0 &&
158*074e084fSml 		    stat(file, &new) != -1 && stat(name, &cur) != -1 &&
159*074e084fSml 		    new.st_dev == cur.st_dev && new.st_ino == cur.st_ino)
160*074e084fSml 			rc = 0;
161*074e084fSml 	}
162*074e084fSml 
163*074e084fSml 	/*
164*074e084fSml 	 * euid 0, egid 0 and the file_dac_write privilege are no longer
165*074e084fSml 	 * required; give them up permanently.
166*074e084fSml 	 */
167*074e084fSml 	(void) priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_FILE_DAC_WRITE, NULL);
168*074e084fSml 	if (setreuid(getuid(), getuid()) == -1 ||
169*074e084fSml 	    setregid(getgid(), getgid()) == -1)
170*074e084fSml 		die(gettext("setreuid()/setregid() failed"));
171*074e084fSml 	if (rc == 0)
1727c478bd9Sstevel@tonic-gate 		return (0);
173*074e084fSml 
174*074e084fSml 	warn(gettext("cannot open %s accounting file %s: %s\n"),
175*074e084fSml 	    ac_type_name(type), file, strerror(err));
176*074e084fSml 	return (-1);
177*074e084fSml }
178*074e084fSml 
179*074e084fSml /*
180*074e084fSml  * Verify that the file contents (if any) are extended accounting records
181*074e084fSml  * of the desired type.
182*074e084fSml  */
183*074e084fSml boolean_t
184*074e084fSml verify_exacct_file(const char *file, int type)
185*074e084fSml {
186*074e084fSml 	ea_file_t ef;
187*074e084fSml 	ea_object_t eo;
188*074e084fSml 	struct stat st;
189*074e084fSml 	int err;
190*074e084fSml 
191*074e084fSml 	if (stat(file, &st) != -1 && st.st_size != 0) {
192*074e084fSml 		if (seteuid(0) == -1)
193*074e084fSml 			return (B_FALSE);
194*074e084fSml 		err = ea_open(&ef, file, "SunOS", EO_TAIL, O_RDONLY, 0);
195*074e084fSml 		if (seteuid(getuid()) == 1)
196*074e084fSml 			die(gettext("seteuid() failed"));
197*074e084fSml 		if (err == -1)
198*074e084fSml 			return (B_FALSE);
199*074e084fSml 
200*074e084fSml 		bzero(&eo, sizeof (eo));
201*074e084fSml 		if (ea_previous_object(&ef, &eo) == EO_ERROR) {
202*074e084fSml 			/*
203*074e084fSml 			 * EXR_EOF indicates there are no non-header objects
204*074e084fSml 			 * in the file.  It can't be determined that this
205*074e084fSml 			 * file is or is not the proper type of extended
206*074e084fSml 			 * accounting file, which isn't necessarily an error.
207*074e084fSml 			 * Since it is a proper (albeit empty) extended
208*074e084fSml 			 * accounting file, it matches any desired type.
209*074e084fSml 			 *
210*074e084fSml 			 * if ea_previous_object() failed for any other reason
211*074e084fSml 			 * than EXR_EOF, the file must be corrupt.
212*074e084fSml 			 */
213*074e084fSml 			if (ea_error() != EXR_EOF) {
214*074e084fSml 				(void) ea_close(&ef);
215*074e084fSml 				return (B_FALSE);
216*074e084fSml 			}
217*074e084fSml 		} else {
218*074e084fSml 			/*
219*074e084fSml 			 * A non-header object exists.  Insist that it be
220*074e084fSml 			 * either a process, task, or flow accounting record,
221*074e084fSml 			 * the same type as is desired.
222*074e084fSml 			 */
223*074e084fSml 			uint_t c = eo.eo_catalog & EXD_DATA_MASK;
224*074e084fSml 
225*074e084fSml 			if (eo.eo_type != EO_GROUP ||
226*074e084fSml 			    (eo.eo_catalog & EXC_CATALOG_MASK) != EXC_NONE ||
227*074e084fSml 			    (!(c == EXD_GROUP_PROC && type == AC_PROC ||
228*074e084fSml 			    c == EXD_GROUP_TASK && type == AC_TASK ||
229*074e084fSml 			    c == EXD_GROUP_FLOW && type == AC_FLOW))) {
230*074e084fSml 				(void) ea_close(&ef);
231*074e084fSml 				return (B_FALSE);
232*074e084fSml 			}
233*074e084fSml 		}
234*074e084fSml 		(void) ea_close(&ef);
2357c478bd9Sstevel@tonic-gate 	}
236*074e084fSml 	return (B_TRUE);
2377c478bd9Sstevel@tonic-gate }
238