xref: /illumos-gate/usr/src/cmd/acctadm/utils.c (revision da14cebe459d3275048785f25bd869cb09b5307f)
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 /*
22074e084fSml  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23d362b749Svk  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
26074e084fSml #include <assert.h>
27074e084fSml #include <sys/types.h>
28074e084fSml #include <sys/acctctl.h>
297c478bd9Sstevel@tonic-gate #include <sys/param.h>
30074e084fSml #include <sys/stat.h>
317c478bd9Sstevel@tonic-gate #include <libintl.h>
327c478bd9Sstevel@tonic-gate #include <string.h>
337c478bd9Sstevel@tonic-gate #include <stdlib.h>
347c478bd9Sstevel@tonic-gate #include <stdarg.h>
357c478bd9Sstevel@tonic-gate #include <stdio.h>
36074e084fSml #include <strings.h>
37074e084fSml #include <unistd.h>
387c478bd9Sstevel@tonic-gate #include <errno.h>
39074e084fSml #include <exacct.h>
40074e084fSml #include <fcntl.h>
41074e084fSml #include <priv.h>
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate #include "utils.h"
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate static char PNAME_FMT[] = "%s: ";
467c478bd9Sstevel@tonic-gate static char ERRNO_FMT[] = ": %s\n";
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate static char *pname;
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate /*PRINTFLIKE1*/
517c478bd9Sstevel@tonic-gate void
52d362b749Svk warn(const char *format, ...)
537c478bd9Sstevel@tonic-gate {
547c478bd9Sstevel@tonic-gate 	int err = errno;
557c478bd9Sstevel@tonic-gate 	va_list alist;
567c478bd9Sstevel@tonic-gate 	if (pname != NULL)
577c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(PNAME_FMT), pname);
587c478bd9Sstevel@tonic-gate 	va_start(alist, format);
597c478bd9Sstevel@tonic-gate 	(void) vfprintf(stderr, format, alist);
607c478bd9Sstevel@tonic-gate 	va_end(alist);
617c478bd9Sstevel@tonic-gate 	if (strchr(format, '\n') == NULL)
627c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERRNO_FMT), strerror(err));
637c478bd9Sstevel@tonic-gate }
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate /*PRINTFLIKE1*/
667c478bd9Sstevel@tonic-gate void
677c478bd9Sstevel@tonic-gate die(char *format, ...)
687c478bd9Sstevel@tonic-gate {
697c478bd9Sstevel@tonic-gate 	int err = errno;
707c478bd9Sstevel@tonic-gate 	va_list alist;
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate 	if (pname != NULL)
737c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(PNAME_FMT), pname);
747c478bd9Sstevel@tonic-gate 	va_start(alist, format);
757c478bd9Sstevel@tonic-gate 	(void) vfprintf(stderr, format, alist);
767c478bd9Sstevel@tonic-gate 	va_end(alist);
777c478bd9Sstevel@tonic-gate 	if (strchr(format, '\n') == NULL)
787c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(ERRNO_FMT), strerror(err));
797c478bd9Sstevel@tonic-gate 	exit(E_ERROR);
807c478bd9Sstevel@tonic-gate }
817c478bd9Sstevel@tonic-gate 
827c478bd9Sstevel@tonic-gate char *
837c478bd9Sstevel@tonic-gate setprogname(char *arg0)
847c478bd9Sstevel@tonic-gate {
857c478bd9Sstevel@tonic-gate 	char *p = strrchr(arg0, '/');
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate 	if (p == NULL)
887c478bd9Sstevel@tonic-gate 		p = arg0;
897c478bd9Sstevel@tonic-gate 	else
907c478bd9Sstevel@tonic-gate 		p++;
917c478bd9Sstevel@tonic-gate 	pname = p;
927c478bd9Sstevel@tonic-gate 	return (pname);
937c478bd9Sstevel@tonic-gate }
947c478bd9Sstevel@tonic-gate 
95074e084fSml /*
96074e084fSml  * Return the localized name of an accounting type.
97074e084fSml  */
98074e084fSml const char *
99074e084fSml ac_type_name(int type)
100074e084fSml {
101074e084fSml 	switch (type) {
102074e084fSml 	case AC_PROC:
103074e084fSml 		return (gettext("process"));
104074e084fSml 	case AC_FLOW:
105074e084fSml 		return (gettext("flow"));
106074e084fSml 	case AC_TASK:
107074e084fSml 		return (gettext("task"));
108*da14cebeSEric Cheng 	case AC_NET:
109*da14cebeSEric Cheng 		return (gettext("net"));
110074e084fSml 	default:
111074e084fSml 		die(gettext("invalid type %d\n"), type);
112074e084fSml 	}
113074e084fSml 	/* NOTREACHED */
114074e084fSml 	return (NULL);
115074e084fSml }
116074e084fSml 
117074e084fSml /*
118074e084fSml  * Open an accounting file.  The filename specified must be an absolute
119074e084fSml  * pathname and the existing contents of the file (if any) must be of the
120074e084fSml  * requested type.  Needs euid 0 to open the root-owned accounting file.
121074e084fSml  * file_dac_write is required to create a new file in a directory not owned
122074e084fSml  * by root (/var/adm/exacct is owned by 'adm').  Assumes sys_acct privilege is
123074e084fSml  * already asserted by caller.
124074e084fSml  */
1257c478bd9Sstevel@tonic-gate int
126074e084fSml open_exacct_file(const char *file, int type)
1277c478bd9Sstevel@tonic-gate {
128074e084fSml 	int rc;
129074e084fSml 	int err;
130074e084fSml 
131074e084fSml 	if (file[0] != '/') {
132074e084fSml 		warn(gettext("%s is not an absolute pathname\n"), file);
133074e084fSml 		return (-1);
1347c478bd9Sstevel@tonic-gate 	}
135074e084fSml 	if (!verify_exacct_file(file, type)) {
136074e084fSml 		warn(gettext("%s is not a %s accounting file\n"), file,
137074e084fSml 		    ac_type_name(type));
138074e084fSml 		return (-1);
139074e084fSml 	}
140074e084fSml 	if (seteuid(0) == -1 || setegid(0) == -1) {
141074e084fSml 		warn(gettext("seteuid()/setegid() failed"));
142074e084fSml 		return (-1);
143074e084fSml 	}
144074e084fSml 	assert(priv_ineffect(PRIV_SYS_ACCT));
145074e084fSml 	(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_FILE_DAC_WRITE, NULL);
146074e084fSml 	rc = acctctl(type | AC_FILE_SET, (void *) file, strlen(file) + 1);
147074e084fSml 	if (rc == -1 && (err = errno) == EBUSY) {
148074e084fSml 		char name[MAXPATHLEN];
149074e084fSml 		struct stat cur;
150074e084fSml 		struct stat new;
151074e084fSml 
152074e084fSml 		/*
153074e084fSml 		 * The file is already open as an accounting file somewhere.
154074e084fSml 		 * If the file we're trying to open is the same as we have
155074e084fSml 		 * currently open then we're ok.
156074e084fSml 		 */
157074e084fSml 		if (acctctl(type | AC_FILE_GET, name, sizeof (name)) == 0 &&
158074e084fSml 		    stat(file, &new) != -1 && stat(name, &cur) != -1 &&
159074e084fSml 		    new.st_dev == cur.st_dev && new.st_ino == cur.st_ino)
160074e084fSml 			rc = 0;
161074e084fSml 	}
162074e084fSml 
163074e084fSml 	/*
164074e084fSml 	 * euid 0, egid 0 and the file_dac_write privilege are no longer
165074e084fSml 	 * required; give them up permanently.
166074e084fSml 	 */
167074e084fSml 	(void) priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_FILE_DAC_WRITE, NULL);
168074e084fSml 	if (setreuid(getuid(), getuid()) == -1 ||
169074e084fSml 	    setregid(getgid(), getgid()) == -1)
170074e084fSml 		die(gettext("setreuid()/setregid() failed"));
171074e084fSml 	if (rc == 0)
1727c478bd9Sstevel@tonic-gate 		return (0);
173074e084fSml 
174074e084fSml 	warn(gettext("cannot open %s accounting file %s: %s\n"),
175074e084fSml 	    ac_type_name(type), file, strerror(err));
176074e084fSml 	return (-1);
177074e084fSml }
178074e084fSml 
179074e084fSml /*
180074e084fSml  * Verify that the file contents (if any) are extended accounting records
181074e084fSml  * of the desired type.
182074e084fSml  */
183074e084fSml boolean_t
184074e084fSml verify_exacct_file(const char *file, int type)
185074e084fSml {
186074e084fSml 	ea_file_t ef;
187074e084fSml 	ea_object_t eo;
188074e084fSml 	struct stat st;
189074e084fSml 	int err;
190074e084fSml 
191074e084fSml 	if (stat(file, &st) != -1 && st.st_size != 0) {
192074e084fSml 		if (seteuid(0) == -1)
193074e084fSml 			return (B_FALSE);
194074e084fSml 		err = ea_open(&ef, file, "SunOS", EO_TAIL, O_RDONLY, 0);
195074e084fSml 		if (seteuid(getuid()) == 1)
196074e084fSml 			die(gettext("seteuid() failed"));
197074e084fSml 		if (err == -1)
198074e084fSml 			return (B_FALSE);
199074e084fSml 
200074e084fSml 		bzero(&eo, sizeof (eo));
201074e084fSml 		if (ea_previous_object(&ef, &eo) == EO_ERROR) {
202074e084fSml 			/*
203074e084fSml 			 * EXR_EOF indicates there are no non-header objects
204074e084fSml 			 * in the file.  It can't be determined that this
205074e084fSml 			 * file is or is not the proper type of extended
206074e084fSml 			 * accounting file, which isn't necessarily an error.
207074e084fSml 			 * Since it is a proper (albeit empty) extended
208074e084fSml 			 * accounting file, it matches any desired type.
209074e084fSml 			 *
210074e084fSml 			 * if ea_previous_object() failed for any other reason
211074e084fSml 			 * than EXR_EOF, the file must be corrupt.
212074e084fSml 			 */
213074e084fSml 			if (ea_error() != EXR_EOF) {
214074e084fSml 				(void) ea_close(&ef);
215074e084fSml 				return (B_FALSE);
216074e084fSml 			}
217074e084fSml 		} else {
218074e084fSml 			/*
219074e084fSml 			 * A non-header object exists.  Insist that it be
220*da14cebeSEric Cheng 			 * either a process, task, flow  or net accounting
221*da14cebeSEric Cheng 			 * record, the same type as is desired.
222*da14cebeSEric Cheng 			 * xxx-venu:check 101 merge for EXD_GROUP_NET_*
223074e084fSml 			 */
224074e084fSml 			uint_t c = eo.eo_catalog & EXD_DATA_MASK;
225074e084fSml 
226074e084fSml 			if (eo.eo_type != EO_GROUP ||
227074e084fSml 			    (eo.eo_catalog & EXC_CATALOG_MASK) != EXC_NONE ||
228074e084fSml 			    (!(c == EXD_GROUP_PROC && type == AC_PROC ||
229074e084fSml 			    c == EXD_GROUP_TASK && type == AC_TASK ||
230*da14cebeSEric Cheng 			    c == EXD_GROUP_FLOW && type == AC_FLOW ||
231*da14cebeSEric Cheng 			    (c == EXD_GROUP_NET_LINK_DESC ||
232*da14cebeSEric Cheng 			    c == EXD_GROUP_NET_FLOW_DESC ||
233*da14cebeSEric Cheng 			    c == EXD_GROUP_NET_LINK_STATS ||
234*da14cebeSEric Cheng 			    c == EXD_GROUP_NET_FLOW_STATS) &&
235*da14cebeSEric Cheng 			    type == AC_NET))) {
236074e084fSml 				(void) ea_close(&ef);
237074e084fSml 				return (B_FALSE);
238074e084fSml 			}
239074e084fSml 		}
240074e084fSml 		(void) ea_close(&ef);
2417c478bd9Sstevel@tonic-gate 	}
242074e084fSml 	return (B_TRUE);
2437c478bd9Sstevel@tonic-gate }
244