xref: /titanic_41/usr/src/cmd/acctadm/main.c (revision a62774df315360f02521d6470eab7d5080137dad)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23   * Use is subject to license terms.
24   */
25  
26  #include <sys/acctctl.h>
27  #include <assert.h>
28  #include <stdio.h>
29  #include <stdlib.h>
30  #include <unistd.h>
31  #include <string.h>
32  #include <errno.h>
33  #include <libintl.h>
34  #include <libdllink.h>
35  #include <locale.h>
36  #include <priv.h>
37  #include <libscf.h>
38  #include <zone.h>
39  
40  #include "utils.h"
41  #include "aconf.h"
42  #include "res.h"
43  
44  #define	ACCTADM_NET_LOG_INTERVAL	20
45  
46  static const char USAGE[] = "\
47  Usage:\n\
48      acctadm [ {process | task | flow | net} ]\n\
49      acctadm -s\n\
50      acctadm -r [ {process | task | flow | net} ]\n\
51      acctadm -x|-E|-D {process | task | flow | net}\n\
52      acctadm -f filename {process | task | flow | net}\n\
53      acctadm -e resources -d resources {process | task | flow | net}\n";
54  
55  static const char OPTS[] = "rsxf:e:d:ED";
56  
57  dladm_handle_t dld_handle = NULL;
58  
59  static void
60  usage()
61  {
62  	(void) fprintf(stderr, gettext(USAGE));
63  	exit(E_USAGE);
64  }
65  
66  static void
67  setup_privs()
68  {
69  	priv_set_t *privset;
70  
71  	if (seteuid(getuid()) == -1 || setegid(getgid()) == -1)
72  		die(gettext("seteuid()/setegid() failed"));
73  
74  	/*
75  	 * Add our privileges and remove unneeded 'basic' privileges from the
76  	 * permitted set.
77  	 */
78  	if ((privset = priv_str_to_set("basic", ",", NULL)) == NULL)
79  		die(gettext("cannot setup privileges"));
80  
81  	(void) priv_addset(privset, PRIV_SYS_ACCT);
82  	(void) priv_addset(privset, PRIV_FILE_DAC_WRITE);
83  	(void) priv_addset(privset, PRIV_SYS_DL_CONFIG);
84  	(void) priv_delset(privset, PRIV_FILE_LINK_ANY);
85  	(void) priv_delset(privset, PRIV_PROC_EXEC);
86  	(void) priv_delset(privset, PRIV_PROC_FORK);
87  	(void) priv_delset(privset, PRIV_PROC_INFO);
88  	(void) priv_delset(privset, PRIV_PROC_SESSION);
89  	priv_inverse(privset);
90  	if (setppriv(PRIV_OFF, PRIV_PERMITTED, privset) == -1)
91  		die(gettext("cannot setup privileges"));
92  	priv_freeset(privset);
93  
94  	/*
95  	 * Clear the Inheritable and Limit sets.
96  	 */
97  	if ((privset = priv_allocset()) == NULL)
98  		die(gettext("cannot setup privileges"));
99  	priv_emptyset(privset);
100  	if (setppriv(PRIV_SET, PRIV_INHERITABLE, privset) == -1 ||
101  	    setppriv(PRIV_SET, PRIV_LIMIT, privset) == -1)
102  		die(gettext("cannot setup privileges"));
103  
104  	/*
105  	 * Turn off the sys_acct, file_dac_write and dl_config privileges
106  	 * until needed.
107  	 */
108  	(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_FILE_DAC_WRITE,
109  	    PRIV_SYS_ACCT, PRIV_SYS_DL_CONFIG, NULL);
110  }
111  
112  int
113  main(int argc, char *argv[])
114  {
115  	int c;			/* options character */
116  	int type = 0;		/* type of accounting */
117  	int modified = 0;	/* have we modified any properties? */
118  	acctconf_t ac;		/* current configuration */
119  	char *typestr = NULL;	/* type of accounting argument string */
120  	char *enabled = NULL;	/* enabled resources string */
121  	char *disabled = NULL;	/* disabled resources string */
122  	char *file = NULL;
123  	int Eflg = 0;
124  	int Dflg = 0;
125  	int rflg = 0;
126  	int sflg = 0;
127  	int xflg = 0;
128  	int optcnt = 0;
129  	int state;
130  	const char *fmri;	/* FMRI for this instance */
131  	int err = 0;
132  
133  	setup_privs();
134  
135  	(void) setlocale(LC_ALL, "");
136  	(void) textdomain(TEXT_DOMAIN);
137  	(void) setprogname(argv[0]);
138  
139  	for (; optind < argc; optind++) {
140  		while ((c = getopt(argc, argv, OPTS)) != (int)EOF) {
141  			switch (c) {
142  			case 'd':
143  				disabled = optarg;
144  				break;
145  			case 'e':
146  				enabled = optarg;
147  				break;
148  			case 'D':
149  				Dflg = 1;
150  				optcnt++;
151  				break;
152  			case 'E':
153  				Eflg = 1;
154  				optcnt++;
155  				break;
156  			case 'f':
157  				file = optarg;
158  				optcnt++;
159  				break;
160  			case 'r':
161  				rflg = 1;
162  				optcnt++;
163  				break;
164  			case 's':
165  				sflg = 1;
166  				optcnt++;
167  				break;
168  			case 'x':
169  				xflg = 1;
170  				optcnt++;
171  				break;
172  			case '?':
173  			default:
174  				usage();
175  			}
176  		}
177  
178  		/*
179  		 * Permanently give up euid 0, egid 0 and privileges we
180  		 * don't need for the specified options.
181  		 */
182  		if (!(file || sflg)) {
183  			if (setreuid(getuid(), getuid()) == -1 ||
184  			    setregid(getgid(), getgid()) == -1)
185  				die(gettext("setreuid()/setregid() failed"));
186  			(void) priv_set(PRIV_OFF, PRIV_PERMITTED,
187  			    PRIV_FILE_DAC_WRITE, NULL);
188  		}
189  		if (!(disabled || enabled || Dflg || Eflg || file || sflg ||
190  		    xflg))
191  			(void) priv_set(PRIV_OFF, PRIV_PERMITTED,
192  			    PRIV_SYS_ACCT, PRIV_SYS_DL_CONFIG, NULL);
193  
194  		if (optind < argc) {
195  			if (typestr != NULL) {
196  				warn(gettext("illegal argument -- %s\n"),
197  				    argv[optind]);
198  				usage();
199  			} else {
200  				typestr = argv[optind];
201  			}
202  		}
203  	}
204  	if (typestr != NULL) {
205  		if (strcmp(typestr, "process") == 0 ||
206  		    strcmp(typestr, "proc") == 0)
207  			type |= AC_PROC;
208  		else if (strcmp(typestr, "task") == 0)
209  			type |= AC_TASK;
210  		else if (strcmp(typestr, "flow") == 0)
211  			type |= AC_FLOW;
212  		else if (strcmp(typestr, "net") == 0)
213  			type |= AC_NET;
214  		else {
215  			warn(gettext("unknown accounting type -- %s\n"),
216  			    typestr);
217  			usage();
218  		}
219  	} else
220  		type = AC_PROC | AC_TASK | AC_FLOW | AC_NET;
221  
222  	/*
223  	 * Drop the DL config privilege if we are not working with
224  	 * net.
225  	 */
226  	if ((type & AC_NET) == 0) {
227  		(void) priv_set(PRIV_OFF, PRIV_PERMITTED,
228  		    PRIV_SYS_DL_CONFIG, NULL);
229  	}
230  	/*
231  	 * check for invalid options
232  	 */
233  	if (optcnt > 1)
234  		usage();
235  
236  	/*
237  	 * XXX For AC_NET, enabled/disabled should only be "basic" or
238  	 * "extended" - need to check it here.
239  	 */
240  	if ((enabled || disabled) && (rflg || Dflg || sflg || xflg || Eflg))
241  		usage();
242  
243  	if ((file || xflg || Dflg || Eflg || enabled || disabled) &&
244  	    !typestr) {
245  		warn(gettext("accounting type must be specified\n"));
246  		usage();
247  	}
248  
249  	if (rflg) {
250  		printgroups(type);
251  		return (E_SUCCESS);
252  	}
253  
254  	/*
255  	 * If no arguments have been passed then just print out the current
256  	 * state and exit.
257  	 */
258  	if (!enabled && !disabled && !file &&
259  	    !Eflg && !rflg && !Dflg && !sflg && !xflg) {
260  		aconf_print(stdout, type);
261  		return (E_SUCCESS);
262  	}
263  
264  	/* Open the libdladm handle */
265  	if (dladm_open(&dld_handle) != DLADM_STATUS_OK)
266  		die(gettext("failed to open dladm handle\n"));
267  
268  	/*
269  	 * smf(5) start method.  The FMRI to operate on is retrieved from the
270  	 * SMF_FMRI environment variable that the restarter provides.
271  	 */
272  	if (sflg) {
273  		if ((fmri = getenv("SMF_FMRI")) != NULL) {
274  			int ret = aconf_setup(fmri);
275  			dladm_close(dld_handle);
276  			return (ret);
277  		}
278  
279  		die(gettext("-s option should only be invoked by smf(5)\n"));
280  	}
281  
282  	assert(type == AC_PROC || type == AC_TASK || type == AC_FLOW ||
283  	    type == AC_NET);
284  
285  	if ((type == AC_FLOW || type == AC_NET) && getzoneid() != GLOBAL_ZONEID)
286  		die(gettext("%s accounting cannot be configured in "
287  		    "non-global zones\n"), ac_type_name(type));
288  
289  	fmri = aconf_type2fmri(type);
290  	if (aconf_scf_init(fmri) == -1)
291  		die(gettext("cannot connect to repository for %s\n"), fmri);
292  
293  	/*
294  	 * Since the sys_acct the privilege allows use of acctctl() regardless
295  	 * of the accounting type, we check the smf(5) authorizations granted
296  	 * to the user to determine whether the user is allowed to change the
297  	 * configuration for this particular accounting type.
298  	 */
299  	if (!aconf_have_smf_auths())
300  		die(gettext("insufficient authorization to change %s extended "
301  		    "accounting configuration\n"), ac_type_name(type));
302  
303  	if (xflg) {
304  		/*
305  		 * Turn off the specified accounting and close its file
306  		 */
307  
308  		/*
309  		 * Stop net logging before turning it off so that the last
310  		 * set of logs can be written.
311  		 */
312  		if (type & AC_NET) {
313  			(void) priv_set(PRIV_ON, PRIV_EFFECTIVE,
314  			    PRIV_SYS_DL_CONFIG, NULL);
315  			err = dladm_stop_usagelog(dld_handle,
316  			    DLADM_LOGTYPE_FLOW);
317  			(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE,
318  			    PRIV_SYS_DL_CONFIG, NULL);
319  			if (err != DLADM_STATUS_OK) {
320  				die(gettext("failed to stop logging network "
321  				    "information, error %d\n"), errno);
322  			}
323  		}
324  		state = AC_OFF;
325  
326  		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
327  		if (acctctl(type | AC_STATE_SET, &state, sizeof (int)) == -1)
328  			die(gettext("cannot disable %s accounting"),
329  			    ac_type_name(type));
330  		if (acctctl(type | AC_FILE_SET, NULL, 0) == -1)
331  			die(gettext("cannot close %s accounting file\n"),
332  			    ac_type_name(type));
333  		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
334  
335  		if (aconf_set_bool(AC_PROP_STATE, B_FALSE) == -1)
336  			die(gettext("cannot update %s property\n"),
337  			    AC_PROP_STATE);
338  		if (aconf_set_string(AC_PROP_FILE, AC_STR_NONE) == -1)
339  			die(gettext("cannot update %s property\n"),
340  			    AC_PROP_FILE);
341  		modified++;
342  	}
343  
344  	if (enabled || disabled) {
345  		char *tracked, *untracked;
346  		ac_res_t *buf;
347  
348  		/*
349  		 * Enable/disable resources
350  		 */
351  		if ((buf = malloc(AC_BUFSIZE)) == NULL)
352  			die(gettext("not enough memory\n"));
353  		(void) memset(buf, 0, AC_BUFSIZE);
354  		if (acctctl(type | AC_RES_GET, buf, AC_BUFSIZE) == -1) {
355  			free(buf);
356  			die(gettext("cannot obtain list of resources\n"));
357  		}
358  		if (disabled) {
359  			/*
360  			 * Stop net logging before turning it off so that the
361  			 * last set of logs can be written.
362  			 */
363  			if (type & AC_NET) {
364  				(void) priv_set(PRIV_ON, PRIV_EFFECTIVE,
365  				    PRIV_SYS_DL_CONFIG, NULL);
366  				err = dladm_stop_usagelog(dld_handle,
367  				    strcmp(disabled, "basic") == 0 ?
368  				    DLADM_LOGTYPE_LINK : DLADM_LOGTYPE_FLOW);
369  				(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE,
370  				    PRIV_SYS_DL_CONFIG, NULL);
371  				if (err != DLADM_STATUS_OK) {
372  					die(gettext("failed to stop logging "
373  					    "network information, error %d\n"),
374  					    errno);
375  				}
376  			}
377  			str2buf(buf, disabled, AC_OFF, type);
378  		}
379  		if (enabled) {
380  			/*
381  			 * Lets us get network logging started.
382  			 */
383  			if (type & AC_NET) {
384  				/*
385  				 * Default logging interval for AC_NET is
386  				 * ACCTADM_NET_LOG_INTERVAL.
387  				 */
388  				(void) priv_set(PRIV_ON, PRIV_EFFECTIVE,
389  				    PRIV_SYS_DL_CONFIG, NULL);
390  				err = dladm_start_usagelog(dld_handle,
391  				    strcmp(enabled, "basic") == 0 ?
392  				    DLADM_LOGTYPE_LINK : DLADM_LOGTYPE_FLOW,
393  				    ACCTADM_NET_LOG_INTERVAL);
394  				(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE,
395  				    PRIV_SYS_DL_CONFIG, NULL);
396  				if (err != DLADM_STATUS_OK) {
397  					die(gettext("failed to start logging "
398  					    "network information, error %d\n"),
399  					    errno);
400  				}
401  			}
402  			str2buf(buf, enabled, AC_ON, type);
403  		}
404  		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
405  		if (acctctl(type | AC_RES_SET, buf, AC_BUFSIZE) == -1) {
406  			free(buf);
407  			die(gettext("cannot enable/disable %s accounting "
408  			    "resources\n"), ac_type_name(type));
409  		}
410  		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
411  
412  		tracked = buf2str(buf, AC_BUFSIZE, AC_ON, type);
413  		untracked = buf2str(buf, AC_BUFSIZE, AC_OFF, type);
414  		if (aconf_set_string(AC_PROP_TRACKED, tracked) == -1)
415  			die(gettext("cannot update %s property\n"),
416  			    AC_PROP_TRACKED);
417  		if (aconf_set_string(AC_PROP_UNTRACKED, untracked) == -1)
418  			die(gettext("cannot update %s property\n"),
419  			    AC_PROP_UNTRACKED);
420  		free(tracked);
421  		free(untracked);
422  		free(buf);
423  		modified++;
424  	}
425  
426  	if (file) {
427  		/*
428  		 * Open new accounting file
429  		 */
430  		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
431  		if (open_exacct_file(file, type) == -1) {
432  			dladm_close(dld_handle);
433  			exit(E_ERROR);
434  		}
435  		if (aconf_set_string(AC_PROP_FILE, file) == -1)
436  			die(gettext("cannot update %s property\n"),
437  			    AC_PROP_FILE);
438  		state = AC_ON;
439  
440  		if (acctctl(type | AC_STATE_SET, &state, sizeof (int)) == -1)
441  			die(gettext("cannot enable %s accounting"),
442  			    ac_type_name(type));
443  		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
444  
445  		if (aconf_set_bool(AC_PROP_STATE, B_TRUE) == -1)
446  			die(gettext("cannot update %s property\n"),
447  			    AC_PROP_STATE);
448  		modified++;
449  	}
450  
451  	if (Dflg) {
452  		/*
453  		 * Disable accounting
454  		 */
455  
456  		/*
457  		 * Stop net logging before turning it off so that the last
458  		 * set of logs can be written.
459  		 */
460  		if (type & AC_NET) {
461  			(void) priv_set(PRIV_ON, PRIV_EFFECTIVE,
462  			    PRIV_SYS_DL_CONFIG, NULL);
463  			err = dladm_stop_usagelog(dld_handle,
464  			    DLADM_LOGTYPE_FLOW);
465  			(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE,
466  			    PRIV_SYS_DL_CONFIG, NULL);
467  			if (err != DLADM_STATUS_OK) {
468  				die(gettext("failed to stop logging "
469  				    "network information, error %d\n"), errno);
470  			}
471  		}
472  		state = AC_OFF;
473  
474  		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
475  		if (acctctl(type | AC_STATE_SET, &state, sizeof (int)) == -1)
476  			die(gettext("cannot disable %s accounting"),
477  			    ac_type_name(type));
478  		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
479  
480  		if (aconf_set_bool(AC_PROP_STATE, B_FALSE) == -1)
481  			die(gettext("cannot update %s property\n"),
482  			    AC_PROP_STATE);
483  		modified++;
484  	}
485  
486  	if (Eflg) {
487  		/*
488  		 * Enable accounting
489  		 */
490  
491  		/*
492  		 * Let's get network logging started.
493  		 */
494  		if (type & AC_NET) {
495  			/*
496  			 * Default logging interval for AC_NET is
497  			 * ACCTADM_NET_LOG_INTERVAL.
498  			 */
499  			(void) priv_set(PRIV_ON, PRIV_EFFECTIVE,
500  			    PRIV_SYS_DL_CONFIG, NULL);
501  			err = dladm_start_usagelog(dld_handle,
502  			    DLADM_LOGTYPE_FLOW, ACCTADM_NET_LOG_INTERVAL);
503  			(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE,
504  			    PRIV_SYS_DL_CONFIG, NULL);
505  			if (err != DLADM_STATUS_OK) {
506  				die(gettext("failed to start logging "
507  				    "network information, error %d\n"), errno);
508  			}
509  		}
510  		state = AC_ON;
511  
512  		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
513  		if (acctctl(type | AC_STATE_SET, &state, sizeof (int)) == -1)
514  			die(gettext("cannot enable %s accounting"),
515  			    ac_type_name(type));
516  		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
517  
518  		if (aconf_set_bool(AC_PROP_STATE, B_TRUE) == -1)
519  			die(gettext("cannot update %s property\n"),
520  			    AC_PROP_STATE);
521  		modified++;
522  	}
523  	(void) priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_SYS_ACCT, NULL);
524  
525  	if (modified) {
526  		char *smf_state;
527  
528  		if (aconf_save() == -1)
529  			die(gettext("cannot save %s accounting "
530  			    "configuration\n"), ac_type_name(type));
531  
532  		/*
533  		 * Enable or disable the instance depending on the effective
534  		 * configuration.  If the effective configuration results in
535  		 * extended accounting being 'on', the instance is enabled so
536  		 * the configuration is applied at the next boot.
537  		 */
538  		smf_state = smf_get_state(fmri);
539  		aconf_init(&ac, type);
540  
541  		if (ac.state == AC_ON ||
542  		    strcmp(ac.file, AC_STR_NONE) != 0 ||
543  		    strcmp(ac.tracked, AC_STR_NONE) != 0) {
544  			if (strcmp(smf_state, SCF_STATE_STRING_ONLINE) != 0)
545  				if (smf_enable_instance(fmri, 0) == -1)
546  					die(gettext("cannot enable %s\n"),
547  					    fmri);
548  		} else {
549  			if (strcmp(smf_state, SCF_STATE_STRING_ONLINE) == 0)
550  				if (smf_disable_instance(fmri, 0) == -1)
551  					die(gettext("cannot disable %s\n"),
552  					    fmri);
553  		}
554  		free(smf_state);
555  	}
556  	aconf_scf_fini();
557  	dladm_close(dld_handle);
558  	return (E_SUCCESS);
559  }
560