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 <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
usage()60 usage()
61 {
62 (void) fprintf(stderr, gettext(USAGE));
63 exit(E_USAGE);
64 }
65
66 static void
setup_privs()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
main(int argc,char * argv[])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) setpname(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 } else if (enabled) {
379 str2buf(buf, enabled, AC_ON, type);
380 }
381 (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
382 if (acctctl(type | AC_RES_SET, buf, AC_BUFSIZE) == -1) {
383 free(buf);
384 die(gettext("cannot enable/disable %s accounting "
385 "resources\n"), ac_type_name(type));
386 }
387 (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
388 tracked = buf2str(buf, AC_BUFSIZE, AC_ON, type);
389 untracked = buf2str(buf, AC_BUFSIZE, AC_OFF, type);
390 if (aconf_set_string(AC_PROP_TRACKED, tracked) == -1)
391 die(gettext("cannot update %s property\n"),
392 AC_PROP_TRACKED);
393 if (aconf_set_string(AC_PROP_UNTRACKED, untracked) == -1)
394 die(gettext("cannot update %s property\n"),
395 AC_PROP_UNTRACKED);
396 free(tracked);
397 free(untracked);
398 free(buf);
399 modified++;
400 }
401
402 if (file) {
403 /*
404 * Open new accounting file
405 */
406 (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
407 if (open_exacct_file(file, type) == -1) {
408 dladm_close(dld_handle);
409 exit(E_ERROR);
410 }
411 if (aconf_set_string(AC_PROP_FILE, file) == -1)
412 die(gettext("cannot update %s property\n"),
413 AC_PROP_FILE);
414 state = AC_ON;
415
416 if (acctctl(type | AC_STATE_SET, &state, sizeof (int)) == -1)
417 die(gettext("cannot enable %s accounting"),
418 ac_type_name(type));
419 (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
420
421 if (aconf_set_bool(AC_PROP_STATE, B_TRUE) == -1)
422 die(gettext("cannot update %s property\n"),
423 AC_PROP_STATE);
424 modified++;
425 }
426
427 /*
428 * Let's get network logging started. We do this after turning on
429 * accounting and opening the file so that we can start writing
430 * immediately.
431 */
432 if (enabled && (type & AC_NET)) {
433 /*
434 * Default logging interval for AC_NET is
435 * ACCTADM_NET_LOG_INTERVAL.
436 */
437 (void) priv_set(PRIV_ON, PRIV_EFFECTIVE,
438 PRIV_SYS_DL_CONFIG, NULL);
439 err = dladm_start_usagelog(dld_handle,
440 strcmp(enabled, "basic") == 0 ?
441 DLADM_LOGTYPE_LINK : DLADM_LOGTYPE_FLOW,
442 ACCTADM_NET_LOG_INTERVAL);
443 (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE,
444 PRIV_SYS_DL_CONFIG, NULL);
445 if (err != DLADM_STATUS_OK) {
446 die(gettext("failed to start logging "
447 "network information, error %d\n"),
448 errno);
449 }
450 }
451
452 if (Dflg) {
453 /*
454 * Disable accounting
455 */
456
457 /*
458 * Stop net logging before turning it off so that the last
459 * set of logs can be written.
460 */
461 if (type & AC_NET) {
462 (void) priv_set(PRIV_ON, PRIV_EFFECTIVE,
463 PRIV_SYS_DL_CONFIG, NULL);
464 err = dladm_stop_usagelog(dld_handle,
465 DLADM_LOGTYPE_FLOW);
466 (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE,
467 PRIV_SYS_DL_CONFIG, NULL);
468 if (err != DLADM_STATUS_OK) {
469 die(gettext("failed to stop logging "
470 "network information, error %d\n"), errno);
471 }
472 }
473 state = AC_OFF;
474
475 (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
476 if (acctctl(type | AC_STATE_SET, &state, sizeof (int)) == -1)
477 die(gettext("cannot disable %s accounting"),
478 ac_type_name(type));
479 (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
480
481 if (aconf_set_bool(AC_PROP_STATE, B_FALSE) == -1)
482 die(gettext("cannot update %s property\n"),
483 AC_PROP_STATE);
484 modified++;
485 }
486
487 if (Eflg) {
488 /*
489 * Enable accounting
490 */
491
492 /*
493 * Let's get network logging started.
494 */
495 if (type & AC_NET) {
496 /*
497 * Default logging interval for AC_NET is
498 * ACCTADM_NET_LOG_INTERVAL.
499 */
500 (void) priv_set(PRIV_ON, PRIV_EFFECTIVE,
501 PRIV_SYS_DL_CONFIG, NULL);
502 err = dladm_start_usagelog(dld_handle,
503 DLADM_LOGTYPE_FLOW, ACCTADM_NET_LOG_INTERVAL);
504 (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE,
505 PRIV_SYS_DL_CONFIG, NULL);
506 if (err != DLADM_STATUS_OK) {
507 die(gettext("failed to start logging "
508 "network information, error %d\n"), errno);
509 }
510 }
511 state = AC_ON;
512
513 (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
514 if (acctctl(type | AC_STATE_SET, &state, sizeof (int)) == -1)
515 die(gettext("cannot enable %s accounting"),
516 ac_type_name(type));
517 (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
518
519 if (aconf_set_bool(AC_PROP_STATE, B_TRUE) == -1)
520 die(gettext("cannot update %s property\n"),
521 AC_PROP_STATE);
522 modified++;
523 }
524 (void) priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_SYS_ACCT, NULL);
525
526 if (modified) {
527 char *smf_state;
528
529 if (aconf_save() == -1)
530 die(gettext("cannot save %s accounting "
531 "configuration\n"), ac_type_name(type));
532
533 /*
534 * Enable or disable the instance depending on the effective
535 * configuration. If the effective configuration results in
536 * extended accounting being 'on', the instance is enabled so
537 * the configuration is applied at the next boot.
538 */
539 smf_state = smf_get_state(fmri);
540 aconf_init(&ac, type);
541
542 if (ac.state == AC_ON ||
543 strcmp(ac.file, AC_STR_NONE) != 0 ||
544 strcmp(ac.tracked, AC_STR_NONE) != 0) {
545 if (strcmp(smf_state, SCF_STATE_STRING_ONLINE) != 0)
546 if (smf_enable_instance(fmri, 0) == -1)
547 die(gettext("cannot enable %s\n"),
548 fmri);
549 } else {
550 if (strcmp(smf_state, SCF_STATE_STRING_ONLINE) == 0)
551 if (smf_disable_instance(fmri, 0) == -1)
552 die(gettext("cannot disable %s\n"),
553 fmri);
554 }
555 free(smf_state);
556 }
557 aconf_scf_fini();
558 dladm_close(dld_handle);
559 return (E_SUCCESS);
560 }
561