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
45static char PNAME_FMT[] = "%s: ";
46static char ERRNO_FMT[] = ": %s\n";
47
48static char *pname;
49
50/*PRINTFLIKE1*/
51void
52warn(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*/
66void
67die(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
87char *
88setpname(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 */
103const char *
104ac_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 */
130int
131open_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 */
188boolean_t
189verify_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