xref: /illumos-gate/usr/src/cmd/acctadm/utils.c (revision 4fe85d41bb4eb0db41934722f4b06c8acec2d25a)
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  #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
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
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  	exit(E_ERROR);
80  }
81  
82  char *
83  setprogname(char *arg0)
84  {
85  	char *p = strrchr(arg0, '/');
86  
87  	if (p == NULL)
88  		p = arg0;
89  	else
90  		p++;
91  	pname = p;
92  	return (pname);
93  }
94  
95  /*
96   * Return the localized name of an accounting type.
97   */
98  const char *
99  ac_type_name(int type)
100  {
101  	switch (type) {
102  	case AC_PROC:
103  		return (gettext("process"));
104  	case AC_FLOW:
105  		return (gettext("flow"));
106  	case AC_TASK:
107  		return (gettext("task"));
108  	case AC_NET:
109  		return (gettext("net"));
110  	default:
111  		die(gettext("invalid type %d\n"), type);
112  	}
113  	/* NOTREACHED */
114  	return (NULL);
115  }
116  
117  /*
118   * Open an accounting file.  The filename specified must be an absolute
119   * pathname and the existing contents of the file (if any) must be of the
120   * requested type.  Needs euid 0 to open the root-owned accounting file.
121   * file_dac_write is required to create a new file in a directory not owned
122   * by root (/var/adm/exacct is owned by 'adm').  Assumes sys_acct privilege is
123   * already asserted by caller.
124   */
125  int
126  open_exacct_file(const char *file, int type)
127  {
128  	int rc;
129  	int err;
130  
131  	if (file[0] != '/') {
132  		warn(gettext("%s is not an absolute pathname\n"), file);
133  		return (-1);
134  	}
135  	if (!verify_exacct_file(file, type)) {
136  		warn(gettext("%s is not a %s accounting file\n"), file,
137  		    ac_type_name(type));
138  		return (-1);
139  	}
140  	if (seteuid(0) == -1 || setegid(0) == -1) {
141  		warn(gettext("seteuid()/setegid() failed"));
142  		return (-1);
143  	}
144  	assert(priv_ineffect(PRIV_SYS_ACCT));
145  	(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_FILE_DAC_WRITE, NULL);
146  	rc = acctctl(type | AC_FILE_SET, (void *) file, strlen(file) + 1);
147  	if (rc == -1 && (err = errno) == EBUSY) {
148  		char name[MAXPATHLEN];
149  		struct stat cur;
150  		struct stat new;
151  
152  		/*
153  		 * The file is already open as an accounting file somewhere.
154  		 * If the file we're trying to open is the same as we have
155  		 * currently open then we're ok.
156  		 */
157  		if (acctctl(type | AC_FILE_GET, name, sizeof (name)) == 0 &&
158  		    stat(file, &new) != -1 && stat(name, &cur) != -1 &&
159  		    new.st_dev == cur.st_dev && new.st_ino == cur.st_ino)
160  			rc = 0;
161  	}
162  
163  	/*
164  	 * euid 0, egid 0 and the file_dac_write privilege are no longer
165  	 * required; give them up permanently.
166  	 */
167  	(void) priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_FILE_DAC_WRITE, NULL);
168  	if (setreuid(getuid(), getuid()) == -1 ||
169  	    setregid(getgid(), getgid()) == -1)
170  		die(gettext("setreuid()/setregid() failed"));
171  	if (rc == 0)
172  		return (0);
173  
174  	warn(gettext("cannot open %s accounting file %s: %s\n"),
175  	    ac_type_name(type), file, strerror(err));
176  	return (-1);
177  }
178  
179  /*
180   * Verify that the file contents (if any) are extended accounting records
181   * of the desired type.
182   */
183  boolean_t
184  verify_exacct_file(const char *file, int type)
185  {
186  	ea_file_t ef;
187  	ea_object_t eo;
188  	struct stat st;
189  	int err;
190  
191  	if (stat(file, &st) != -1 && st.st_size != 0) {
192  		if (seteuid(0) == -1)
193  			return (B_FALSE);
194  		err = ea_open(&ef, file, "SunOS", EO_TAIL, O_RDONLY, 0);
195  		if (seteuid(getuid()) == 1)
196  			die(gettext("seteuid() failed"));
197  		if (err == -1)
198  			return (B_FALSE);
199  
200  		bzero(&eo, sizeof (eo));
201  		if (ea_previous_object(&ef, &eo) == EO_ERROR) {
202  			/*
203  			 * EXR_EOF indicates there are no non-header objects
204  			 * in the file.  It can't be determined that this
205  			 * file is or is not the proper type of extended
206  			 * accounting file, which isn't necessarily an error.
207  			 * Since it is a proper (albeit empty) extended
208  			 * accounting file, it matches any desired type.
209  			 *
210  			 * if ea_previous_object() failed for any other reason
211  			 * than EXR_EOF, the file must be corrupt.
212  			 */
213  			if (ea_error() != EXR_EOF) {
214  				(void) ea_close(&ef);
215  				return (B_FALSE);
216  			}
217  		} else {
218  			/*
219  			 * A non-header object exists.  Insist that it be
220  			 * either a process, task, flow  or net accounting
221  			 * record, the same type as is desired.
222  			 * xxx-venu:check 101 merge for EXD_GROUP_NET_*
223  			 */
224  			uint_t c = eo.eo_catalog & EXD_DATA_MASK;
225  
226  			if (eo.eo_type != EO_GROUP ||
227  			    (eo.eo_catalog & EXC_CATALOG_MASK) != EXC_NONE ||
228  			    (!(c == EXD_GROUP_PROC && type == AC_PROC ||
229  			    c == EXD_GROUP_TASK && type == AC_TASK ||
230  			    c == EXD_GROUP_FLOW && type == AC_FLOW ||
231  			    (c == EXD_GROUP_NET_LINK_DESC ||
232  			    c == EXD_GROUP_NET_FLOW_DESC ||
233  			    c == EXD_GROUP_NET_LINK_STATS ||
234  			    c == EXD_GROUP_NET_FLOW_STATS) &&
235  			    type == AC_NET))) {
236  				(void) ea_close(&ef);
237  				return (B_FALSE);
238  			}
239  		}
240  		(void) ea_close(&ef);
241  	}
242  	return (B_TRUE);
243  }
244