xref: /illumos-gate/usr/src/cmd/acctadm/aconf.c (revision 3dbfc80346c4b24f1337e411111b9521c729cf9e)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/types.h>
29 #include <sys/acctctl.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <limits.h>
35 #include <libscf.h>
36 #include <pwd.h>
37 #include <auth_attr.h>
38 #include <nss_dbdefs.h>
39 #include <secdb.h>
40 #include <priv.h>
41 #include <zone.h>
42 
43 #include "aconf.h"
44 #include "utils.h"
45 #include "res.h"
46 
47 #define	FMRI_FLOW_ACCT	"svc:/system/extended-accounting:flow"
48 #define	FMRI_PROC_ACCT	"svc:/system/extended-accounting:process"
49 #define	FMRI_TASK_ACCT	"svc:/system/extended-accounting:task"
50 
51 #define	NELEM(x)	(sizeof (x)) / (sizeof (x[0]))
52 
53 typedef struct props {
54 	char *propname;
55 	int proptype;
56 	scf_transaction_entry_t *entry;
57 	scf_value_t *value;
58 	struct props *next;
59 } props_t;
60 
61 static void	aconf_print_type(acctconf_t *, FILE *, int);
62 static int	aconf_get_bool(const char *, const char *, uint8_t *);
63 static int	aconf_get_string(const char *, const char *, char *, size_t);
64 static props_t	*aconf_prop(const char *, int);
65 static int	aconf_fmri2type(const char *);
66 
67 static scf_handle_t	*handle = NULL;
68 static scf_instance_t	*inst = NULL;
69 static props_t		*props = NULL;
70 
71 void
72 aconf_init(acctconf_t *acp, int type)
73 {
74 	void *buf;
75 	char *tracked;
76 	char *untracked;
77 
78 	if ((buf = malloc(AC_BUFSIZE)) == NULL)
79 		die(gettext("not enough memory\n"));
80 
81 	if (acctctl(type | AC_STATE_GET, &acp->state,
82 	    sizeof (acp->state)) == -1)
83 		die(gettext("cannot get %s accounting state\n"),
84 		    ac_type_name(type));
85 
86 	(void) memset(acp->file, 0, sizeof (acp->file));
87 	if (acctctl(type | AC_FILE_GET, acp->file, sizeof (acp->file)) == -1) {
88 		if (errno == ENOTACTIVE)
89 			(void) strlcpy(acp->file, AC_STR_NONE,
90 			    sizeof (acp->file));
91 		else
92 			die(gettext("cannot get %s accounting file name"),
93 			    ac_type_name(type));
94 	}
95 	(void) memset(buf, 0, AC_BUFSIZE);
96 	if (acctctl(type | AC_RES_GET, buf, AC_BUFSIZE) == -1)
97 		die(gettext("cannot obtain the list of enabled resources\n"));
98 
99 	tracked = buf2str(buf, AC_BUFSIZE, AC_ON, type);
100 	untracked = buf2str(buf, AC_BUFSIZE, AC_OFF, type);
101 	(void) strlcpy(acp->tracked, tracked, sizeof (acp->tracked));
102 	(void) strlcpy(acp->untracked, untracked, sizeof (acp->untracked));
103 	free(tracked);
104 	free(untracked);
105 	free(buf);
106 }
107 
108 /*
109  * SMF start method: configure extended accounting from properties stored in
110  * the repository.  Any errors encountered while retrieving properties from
111  * the repository, such as missing properties or properties of the wrong type,
112  * are fatal as they indicate severe damage to the service (all required
113  * properties are delivered in the service manifest and should thus always be
114  * present).  No attempts will be made to repair such damage;  the service will
115  * be forced into maintenance state by returning SMF_EXIT_ERR_CONFIG.  For all
116  * other errors we we try to configure as much as possible and return
117  * SMF_EXIT_ERR_FATAL.
118  */
119 int
120 aconf_setup(const char *fmri)
121 {
122 	char file[MAXPATHLEN];
123 	char tracked[MAXRESLEN];
124 	char untracked[MAXRESLEN];
125 	void *buf;
126 	int type;
127 	int state;
128 	uint8_t b;
129 	int ret = SMF_EXIT_OK;
130 
131 	if ((type = aconf_fmri2type(fmri)) == -1) {
132 		warn(gettext("no accounting type for %s\n"), fmri);
133 		return (SMF_EXIT_ERR_FATAL);
134 	}
135 
136 	/*
137 	 * Flow accounting is not available in non-global zones and
138 	 * the service instance should therefore never be 'enabled' in
139 	 * non-global zones.  This is enforced by acctadm(1M), but there is
140 	 * nothing that prevents someone from calling svcadm enable directly,
141 	 * so we handle that case here by disabling the instance.
142 	 */
143 	if (type == AC_FLOW && getzoneid() != GLOBAL_ZONEID) {
144 		(void) smf_disable_instance(fmri, 0);
145 		warn(gettext("%s accounting cannot be configured in "
146 		    "non-global zones\n"), ac_type_name(type));
147 		return (SMF_EXIT_OK);
148 	}
149 
150 	if (aconf_scf_init(fmri) == -1) {
151 		warn(gettext("cannot connect to repository\n"));
152 		return (SMF_EXIT_ERR_FATAL);
153 	}
154 	if (aconf_get_string(AC_PGNAME, AC_PROP_TRACKED, tracked,
155 	    sizeof (tracked)) == -1) {
156 		warn(gettext("cannot get %s property\n"), AC_PROP_TRACKED);
157 		ret = SMF_EXIT_ERR_CONFIG;
158 		goto out;
159 	}
160 	if (aconf_get_string(AC_PGNAME, AC_PROP_UNTRACKED, untracked,
161 	    sizeof (untracked)) == -1) {
162 		warn(gettext("cannot get %s property\n"), AC_PROP_UNTRACKED);
163 		ret = SMF_EXIT_ERR_CONFIG;
164 		goto out;
165 	}
166 	if (aconf_get_string(AC_PGNAME, AC_PROP_FILE, file,
167 	    sizeof (file)) == -1) {
168 		warn(gettext("cannot get %s property\n"), AC_PROP_FILE);
169 		ret = SMF_EXIT_ERR_CONFIG;
170 		goto out;
171 	}
172 	if (aconf_get_bool(AC_PGNAME, AC_PROP_STATE, &b) == -1) {
173 		warn(gettext("cannot get %s property\n"), AC_PROP_STATE);
174 		ret = SMF_EXIT_ERR_CONFIG;
175 		goto out;
176 	}
177 	state = (b ? AC_ON : AC_OFF);
178 
179 	if ((buf = malloc(AC_BUFSIZE)) == NULL) {
180 		warn(gettext("not enough memory\n"));
181 		ret = SMF_EXIT_ERR_FATAL;
182 		goto out;
183 	}
184 	(void) memset(buf, 0, AC_BUFSIZE);
185 	str2buf(buf, untracked, AC_OFF, type);
186 	str2buf(buf, tracked, AC_ON, type);
187 
188 	(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
189 	if (acctctl(type | AC_RES_SET, buf, AC_BUFSIZE) == -1) {
190 		warn(gettext("cannot enable/disable %s accounting resources"),
191 		    ac_type_name(type));
192 		ret = SMF_EXIT_ERR_FATAL;
193 	}
194 	free(buf);
195 
196 	if (strcmp(file, AC_STR_NONE) != 0) {
197 		if (open_exacct_file(file, type) == -1)
198 			ret = SMF_EXIT_ERR_FATAL;
199 	} else {
200 		if (acctctl(type | AC_FILE_SET, NULL, 0) == -1) {
201 			warn(gettext("cannot close %s accounting file"),
202 			    ac_type_name(type));
203 			ret = SMF_EXIT_ERR_FATAL;
204 		}
205 	}
206 	if (acctctl(type | AC_STATE_SET, &state, sizeof (state)) == -1) {
207 		warn(gettext("cannot %s %s accounting"),
208 		    state == AC_ON ? gettext("enable") : gettext("disable"),
209 		    ac_type_name(type));
210 		ret = SMF_EXIT_ERR_FATAL;
211 	}
212 	(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
213 out:
214 	aconf_scf_fini();
215 	return (ret);
216 }
217 
218 void
219 aconf_print(FILE *fp, int types)
220 {
221 	acctconf_t ac;
222 	int print_order[] = { AC_TASK, AC_PROC, AC_FLOW };
223 	int i;
224 
225 	for (i = 0; i < NELEM(print_order); i++) {
226 		if (types & print_order[i]) {
227 			aconf_init(&ac, print_order[i]);
228 			aconf_print_type(&ac, fp, print_order[i]);
229 		}
230 	}
231 }
232 
233 static void
234 aconf_print_type(acctconf_t *acp, FILE *fp, int type)
235 {
236 	switch (type) {
237 	case AC_TASK:
238 		(void) fprintf(fp,
239 		    gettext("            Task accounting: %s\n"),
240 		    acp->state == AC_ON ?
241 		    gettext("active") : gettext("inactive"));
242 		(void) fprintf(fp,
243 		    gettext("       Task accounting file: %s\n"),
244 		    acp->file);
245 		(void) fprintf(fp,
246 		    gettext("     Tracked task resources: %s\n"),
247 		    acp->tracked);
248 		(void) fprintf(fp,
249 		    gettext("   Untracked task resources: %s\n"),
250 		    acp->untracked);
251 		break;
252 	case AC_PROC:
253 		(void) fprintf(fp,
254 		    gettext("         Process accounting: %s\n"),
255 		    acp->state == AC_ON ?
256 		    gettext("active") : gettext("inactive"));
257 		(void) fprintf(fp,
258 		    gettext("    Process accounting file: %s\n"),
259 		    acp->file);
260 		(void) fprintf(fp,
261 		    gettext("  Tracked process resources: %s\n"),
262 		    acp->tracked);
263 		(void) fprintf(fp,
264 		    gettext("Untracked process resources: %s\n"),
265 		    acp->untracked);
266 		break;
267 	case AC_FLOW:
268 		(void) fprintf(fp,
269 		    gettext("            Flow accounting: %s\n"),
270 		    acp->state == AC_ON ?
271 		    gettext("active") : gettext("inactive"));
272 		(void) fprintf(fp,
273 		    gettext("       Flow accounting file: %s\n"),
274 		    acp->file);
275 		(void) fprintf(fp,
276 		    gettext("     Tracked flow resources: %s\n"),
277 		    acp->tracked);
278 		(void) fprintf(fp,
279 		    gettext("   Untracked flow resources: %s\n"),
280 		    acp->untracked);
281 		break;
282 	}
283 }
284 
285 /*
286  * Modified properties are put on the 'props' linked list by aconf_set_string()
287  * and aconf_set_bool().  Walk the list of modified properties and write them
288  * to the repository.  The list is deleted on exit.
289  */
290 int
291 aconf_save(void)
292 {
293 	scf_propertygroup_t *pg;
294 	scf_transaction_t *tx;
295 	props_t *p;
296 	props_t *q;
297 	int tx_result;
298 
299 	if (props == NULL)
300 		return (0);
301 
302 	if ((pg = scf_pg_create(handle)) == NULL ||
303 	    scf_instance_get_pg(inst, AC_PGNAME, pg) == -1 ||
304 	    (tx = scf_transaction_create(handle)) == NULL)
305 		goto out;
306 
307 	do {
308 		if (scf_pg_update(pg) == -1 ||
309 		    scf_transaction_start(tx, pg) == -1)
310 			goto out;
311 
312 		for (p = props; p != NULL; p = p->next) {
313 			if (scf_transaction_property_change(tx, p->entry,
314 			    p->propname, p->proptype) == -1)
315 				goto out;
316 			(void) scf_entry_add_value(p->entry, p->value);
317 		}
318 		tx_result = scf_transaction_commit(tx);
319 		scf_transaction_reset(tx);
320 	} while (tx_result == 0);
321 
322 out:
323 	p = props;
324 	while (p != NULL) {
325 		scf_value_destroy(p->value);
326 		scf_entry_destroy(p->entry);
327 		free(p->propname);
328 		q = p->next;
329 		free(p);
330 		p = q;
331 	}
332 	props = NULL;
333 	scf_transaction_destroy(tx);
334 	scf_pg_destroy(pg);
335 	return ((tx_result == 1) ? 0 : -1);
336 }
337 
338 boolean_t
339 aconf_have_smf_auths(void)
340 {
341 	char auth[NSS_BUFLEN_AUTHATTR];
342 	struct passwd *pw;
343 
344 	if ((pw = getpwuid(getuid())) == NULL)
345 		return (B_FALSE);
346 
347 	if (aconf_get_string("general", "action_authorization", auth,
348 	    sizeof (auth)) == -1 || chkauthattr(auth, pw->pw_name) == 0)
349 		return (B_FALSE);
350 
351 	if (aconf_get_string("general", "value_authorization", auth,
352 	    sizeof (auth)) == -1 || chkauthattr(auth, pw->pw_name) == 0)
353 		return (B_FALSE);
354 
355 	if (aconf_get_string("config", "value_authorization", auth,
356 	    sizeof (auth)) == -1 || chkauthattr(auth, pw->pw_name) == 0)
357 		return (B_FALSE);
358 
359 	return (B_TRUE);
360 }
361 
362 const char *
363 aconf_type2fmri(int type)
364 {
365 	switch (type) {
366 	case AC_PROC:
367 		return (FMRI_PROC_ACCT);
368 	case AC_TASK:
369 		return (FMRI_TASK_ACCT);
370 	case AC_FLOW:
371 		return (FMRI_FLOW_ACCT);
372 	default:
373 		die(gettext("invalid type %d\n"), type);
374 	}
375 	/* NOTREACHED */
376 	return (NULL);
377 }
378 
379 static int
380 aconf_fmri2type(const char *fmri)
381 {
382 	if (strcmp(fmri, FMRI_PROC_ACCT) == 0)
383 		return (AC_PROC);
384 	else if (strcmp(fmri, FMRI_TASK_ACCT) == 0)
385 		return (AC_TASK);
386 	else if (strcmp(fmri, FMRI_FLOW_ACCT) == 0)
387 		return (AC_FLOW);
388 	else
389 		return (-1);
390 }
391 
392 int
393 aconf_scf_init(const char *fmri)
394 {
395 	if ((handle = scf_handle_create(SCF_VERSION)) == NULL ||
396 	    scf_handle_bind(handle) == -1 ||
397 	    (inst = scf_instance_create(handle)) == NULL ||
398 	    scf_handle_decode_fmri(handle, fmri, NULL, NULL, inst, NULL, NULL,
399 	    SCF_DECODE_FMRI_EXACT) == -1) {
400 		aconf_scf_fini();
401 		return (-1);
402 	}
403 	return (0);
404 }
405 
406 void
407 aconf_scf_fini(void)
408 {
409 	scf_instance_destroy(inst);
410 	(void) scf_handle_unbind(handle);
411 	scf_handle_destroy(handle);
412 }
413 
414 static int
415 aconf_get_string(const char *pgname, const char *propname, char *buf,
416     size_t len)
417 {
418 	scf_propertygroup_t *pg;
419 	scf_property_t *prop;
420 	scf_value_t *value;
421 	int ret = 0;
422 
423 	if ((pg = scf_pg_create(handle)) == NULL)
424 		return (-1);
425 
426 	if (scf_instance_get_pg_composed(inst, NULL, pgname, pg) == -1) {
427 		scf_pg_destroy(pg);
428 		return (-1);
429 	}
430 
431 	if ((prop = scf_property_create(handle)) == NULL ||
432 	    (value = scf_value_create(handle)) == NULL ||
433 	    scf_pg_get_property(pg, propname, prop) == -1 ||
434 	    scf_property_get_value(prop, value) == -1 ||
435 	    scf_value_get_astring(value, buf, len) == -1)
436 		ret = -1;
437 
438 	scf_value_destroy(value);
439 	scf_property_destroy(prop);
440 	scf_pg_destroy(pg);
441 	return (ret);
442 }
443 
444 static int
445 aconf_get_bool(const char *pgname, const char *propname, uint8_t *rval)
446 {
447 	scf_propertygroup_t *pg;
448 	scf_property_t *prop;
449 	scf_value_t *value;
450 	int ret = 0;
451 
452 	if ((pg = scf_pg_create(handle)) == NULL)
453 		return (-1);
454 
455 	if (scf_instance_get_pg_composed(inst, NULL, pgname, pg) == -1) {
456 		scf_pg_destroy(pg);
457 		return (-1);
458 	}
459 
460 	if ((prop = scf_property_create(handle)) == NULL ||
461 	    (value = scf_value_create(handle)) == NULL ||
462 	    scf_pg_get_property(pg, propname, prop) == -1 ||
463 	    scf_property_get_value(prop, value) == -1 ||
464 	    scf_value_get_boolean(value, rval) == -1)
465 		ret = -1;
466 
467 	scf_value_destroy(value);
468 	scf_property_destroy(prop);
469 	scf_pg_destroy(pg);
470 	return (ret);
471 }
472 
473 int
474 aconf_set_string(const char *propname, const char *value)
475 {
476 	props_t *p;
477 
478 	if ((p = aconf_prop(propname, SCF_TYPE_ASTRING)) == NULL)
479 		return (-1);
480 
481 	if (scf_value_set_astring(p->value, value) == -1)
482 		return (-1);
483 	return (0);
484 }
485 
486 int
487 aconf_set_bool(const char *propname, boolean_t value)
488 {
489 	props_t *p;
490 
491 	if ((p = aconf_prop(propname, SCF_TYPE_BOOLEAN)) == NULL)
492 		return (-1);
493 
494 	scf_value_set_boolean(p->value, value);
495 	return (0);
496 }
497 
498 static props_t *
499 aconf_prop(const char *propname, int proptype)
500 {
501 	props_t *p;
502 
503 	if ((p = malloc(sizeof (props_t))) != NULL) {
504 		if ((p->propname = strdup(propname)) == NULL) {
505 			free(p);
506 			return (NULL);
507 		}
508 		if ((p->entry = scf_entry_create(handle)) == NULL) {
509 			free(p->propname);
510 			free(p);
511 			return (NULL);
512 		}
513 		if ((p->value = scf_value_create(handle)) == NULL) {
514 			scf_entry_destroy(p->entry);
515 			free(p->propname);
516 			free(p);
517 			return (NULL);
518 		}
519 		p->proptype = proptype;
520 		p->next = props;
521 		props = p;
522 	}
523 	return (p);
524 }
525