xref: /illumos-gate/usr/src/cmd/acctadm/utils.c (revision 6a634c9d)
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 /*
23  * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 #include <assert.h>
27 #include <sys/types.h>
28 #include <sys/acctctl.h>
29 #include <sys/param.h>
30 #include <sys/stat.h>
31 #include <libintl.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <strings.h>
37 #include <unistd.h>
38 #include <errno.h>
39 #include <exacct.h>
40 #include <fcntl.h>
41 #include <priv.h>
42 
43 #include "utils.h"
44 
45 static char PNAME_FMT[] = "%s: ";
46 static char ERRNO_FMT[] = ": %s\n";
47 
48 static char *pname;
49 
50 /*PRINTFLIKE1*/
51 void
warn(const char * format,...)52 warn(const char *format, ...)
53 {
54 	int err = errno;
55 	va_list alist;
56 	if (pname != NULL)
57 		(void) fprintf(stderr, gettext(PNAME_FMT), pname);
58 	va_start(alist, format);
59 	(void) vfprintf(stderr, format, alist);
60 	va_end(alist);
61 	if (strchr(format, '\n') == NULL)
62 		(void) fprintf(stderr, gettext(ERRNO_FMT), strerror(err));
63 }
64 
65 /*PRINTFLIKE1*/
66 void
die(char * format,...)67 die(char *format, ...)
68 {
69 	int err = errno;
70 	va_list alist;
71 
72 	if (pname != NULL)
73 		(void) fprintf(stderr, gettext(PNAME_FMT), pname);
74 	va_start(alist, format);
75 	(void) vfprintf(stderr, format, alist);
76 	va_end(alist);
77 	if (strchr(format, '\n') == NULL)
78 		(void) fprintf(stderr, gettext(ERRNO_FMT), strerror(err));
79 
80 	/* close the libdladm handle if it was opened */
81 	if (dld_handle != NULL)
82 		dladm_close(dld_handle);
83 
84 	exit(E_ERROR);
85 }
86 
87 char *
setpname(char * arg0)88 setpname(char *arg0)
89 {
90 	char *p = strrchr(arg0, '/');
91 
92 	if (p == NULL)
93 		p = arg0;
94 	else
95 		p++;
96 	pname = p;
97 	return (pname);
98 }
99 
100 /*
101  * Return the localized name of an accounting type.
102  */
103 const char *
ac_type_name(int type)104 ac_type_name(int type)
105 {
106 	switch (type) {
107 	case AC_PROC:
108 		return (gettext("process"));
109 	case AC_FLOW:
110 		return (gettext("flow"));
111 	case AC_TASK:
112 		return (gettext("task"));
113 	case AC_NET:
114 		return (gettext("net"));
115 	default:
116 		die(gettext("invalid type %d\n"), type);
117 	}
118 	/* NOTREACHED */
119 	return (NULL);
120 }
121 
122 /*
123  * Open an accounting file.  The filename specified must be an absolute
124  * pathname and the existing contents of the file (if any) must be of the
125  * requested type.  Needs euid 0 to open the root-owned accounting file.
126  * file_dac_write is required to create a new file in a directory not owned
127  * by root (/var/adm/exacct is owned by 'adm').  Assumes sys_acct privilege is
128  * already asserted by caller.
129  */
130 int
open_exacct_file(const char * file,int type)131 open_exacct_file(const char *file, int type)
132 {
133 	int rc;
134 	int err;
135 
136 	if (file[0] != '/') {
137 		warn(gettext("%s is not an absolute pathname\n"), file);
138 		return (-1);
139 	}
140 	if (!verify_exacct_file(file, type)) {
141 		warn(gettext("%s is not a %s accounting file\n"), file,
142 		    ac_type_name(type));
143 		return (-1);
144 	}
145 	if (seteuid(0) == -1 || setegid(0) == -1) {
146 		warn(gettext("seteuid()/setegid() failed"));
147 		return (-1);
148 	}
149 	assert(priv_ineffect(PRIV_SYS_ACCT));
150 	(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_FILE_DAC_WRITE, NULL);
151 	rc = acctctl(type | AC_FILE_SET, (void *) file, strlen(file) + 1);
152 	if (rc == -1 && (err = errno) == EBUSY) {
153 		char name[MAXPATHLEN];
154 		struct stat cur;
155 		struct stat new;
156 
157 		/*
158 		 * The file is already open as an accounting file somewhere.
159 		 * If the file we're trying to open is the same as we have
160 		 * currently open then we're ok.
161 		 */
162 		if (acctctl(type | AC_FILE_GET, name, sizeof (name)) == 0 &&
163 		    stat(file, &new) != -1 && stat(name, &cur) != -1 &&
164 		    new.st_dev == cur.st_dev && new.st_ino == cur.st_ino)
165 			rc = 0;
166 	}
167 
168 	/*
169 	 * euid 0, egid 0 and the file_dac_write privilege are no longer
170 	 * required; give them up permanently.
171 	 */
172 	(void) priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_FILE_DAC_WRITE, NULL);
173 	if (setreuid(getuid(), getuid()) == -1 ||
174 	    setregid(getgid(), getgid()) == -1)
175 		die(gettext("setreuid()/setregid() failed"));
176 	if (rc == 0)
177 		return (0);
178 
179 	warn(gettext("cannot open %s accounting file %s: %s\n"),
180 	    ac_type_name(type), file, strerror(err));
181 	return (-1);
182 }
183 
184 /*
185  * Verify that the file contents (if any) are extended accounting records
186  * of the desired type.
187  */
188 boolean_t
verify_exacct_file(const char * file,int type)189 verify_exacct_file(const char *file, int type)
190 {
191 	ea_file_t ef;
192 	ea_object_t eo;
193 	struct stat st;
194 	int err;
195 
196 	if (stat(file, &st) != -1 && st.st_size != 0) {
197 		if (seteuid(0) == -1)
198 			return (B_FALSE);
199 		err = ea_open(&ef, file, "SunOS", EO_TAIL, O_RDONLY, 0);
200 		if (seteuid(getuid()) == 1)
201 			die(gettext("seteuid() failed"));
202 		if (err == -1)
203 			return (B_FALSE);
204 
205 		bzero(&eo, sizeof (eo));
206 		if (ea_previous_object(&ef, &eo) == EO_ERROR) {
207 			/*
208 			 * EXR_EOF indicates there are no non-header objects
209 			 * in the file.  It can't be determined that this
210 			 * file is or is not the proper type of extended
211 			 * accounting file, which isn't necessarily an error.
212 			 * Since it is a proper (albeit empty) extended
213 			 * accounting file, it matches any desired type.
214 			 *
215 			 * if ea_previous_object() failed for any other reason
216 			 * than EXR_EOF, the file must be corrupt.
217 			 */
218 			if (ea_error() != EXR_EOF) {
219 				(void) ea_close(&ef);
220 				return (B_FALSE);
221 			}
222 		} else {
223 			/*
224 			 * A non-header object exists.  Insist that it be
225 			 * either a process, task, flow  or net accounting
226 			 * record, the same type as is desired.
227 			 * xxx-venu:check 101 merge for EXD_GROUP_NET_*
228 			 */
229 			uint_t c = eo.eo_catalog & EXD_DATA_MASK;
230 
231 			if (eo.eo_type != EO_GROUP ||
232 			    (eo.eo_catalog & EXC_CATALOG_MASK) != EXC_NONE ||
233 			    (!(c == EXD_GROUP_PROC && type == AC_PROC ||
234 			    c == EXD_GROUP_TASK && type == AC_TASK ||
235 			    c == EXD_GROUP_FLOW && type == AC_FLOW ||
236 			    (c == EXD_GROUP_NET_LINK_DESC ||
237 			    c == EXD_GROUP_NET_FLOW_DESC ||
238 			    c == EXD_GROUP_NET_LINK_STATS ||
239 			    c == EXD_GROUP_NET_FLOW_STATS) &&
240 			    type == AC_NET))) {
241 				(void) ea_close(&ef);
242 				return (B_FALSE);
243 			}
244 		}
245 		(void) ea_close(&ef);
246 	}
247 	return (B_TRUE);
248 }
249