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