#include <assert.h>
#include <sys/types.h>
#include <sys/acctctl.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <libintl.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <strings.h>
#include <unistd.h>
#include <errno.h>
#include <exacct.h>
#include <fcntl.h>
#include <priv.h>
#include "utils.h"
static char PNAME_FMT[] = "%s: ";
static char ERRNO_FMT[] = ": %s\n";
static char *pname;
void
warn(const char *format, ...)
{
int err = errno;
va_list alist;
if (pname != NULL)
(void) fprintf(stderr, gettext(PNAME_FMT), pname);
va_start(alist, format);
(void) vfprintf(stderr, format, alist);
va_end(alist);
if (strchr(format, '\n') == NULL)
(void) fprintf(stderr, gettext(ERRNO_FMT), strerror(err));
}
void
die(char *format, ...)
{
int err = errno;
va_list alist;
if (pname != NULL)
(void) fprintf(stderr, gettext(PNAME_FMT), pname);
va_start(alist, format);
(void) vfprintf(stderr, format, alist);
va_end(alist);
if (strchr(format, '\n') == NULL)
(void) fprintf(stderr, gettext(ERRNO_FMT), strerror(err));
if (dld_handle != NULL)
dladm_close(dld_handle);
exit(E_ERROR);
}
char *
setpname(char *arg0)
{
char *p = strrchr(arg0, '/');
if (p == NULL)
p = arg0;
else
p++;
pname = p;
return (pname);
}
const char *
ac_type_name(int type)
{
switch (type) {
case AC_PROC:
return (gettext("process"));
case AC_FLOW:
return (gettext("flow"));
case AC_TASK:
return (gettext("task"));
case AC_NET:
return (gettext("net"));
default:
die(gettext("invalid type %d\n"), type);
}
return (NULL);
}
int
open_exacct_file(const char *file, int type)
{
int rc;
int err;
if (file[0] != '/') {
warn(gettext("%s is not an absolute pathname\n"), file);
return (-1);
}
if (!verify_exacct_file(file, type)) {
warn(gettext("%s is not a %s accounting file\n"), file,
ac_type_name(type));
return (-1);
}
if (seteuid(0) == -1 || setegid(0) == -1) {
warn(gettext("seteuid()/setegid() failed"));
return (-1);
}
assert(priv_ineffect(PRIV_SYS_ACCT));
(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_FILE_DAC_WRITE, NULL);
rc = acctctl(type | AC_FILE_SET, (void *) file, strlen(file) + 1);
if (rc == -1 && (err = errno) == EBUSY) {
char name[MAXPATHLEN];
struct stat cur;
struct stat new;
if (acctctl(type | AC_FILE_GET, name, sizeof (name)) == 0 &&
stat(file, &new) != -1 && stat(name, &cur) != -1 &&
new.st_dev == cur.st_dev && new.st_ino == cur.st_ino)
rc = 0;
}
(void) priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_FILE_DAC_WRITE, NULL);
if (setreuid(getuid(), getuid()) == -1 ||
setregid(getgid(), getgid()) == -1)
die(gettext("setreuid()/setregid() failed"));
if (rc == 0)
return (0);
warn(gettext("cannot open %s accounting file %s: %s\n"),
ac_type_name(type), file, strerror(err));
return (-1);
}
boolean_t
verify_exacct_file(const char *file, int type)
{
ea_file_t ef;
ea_object_t eo;
struct stat st;
int err;
if (stat(file, &st) != -1 && st.st_size != 0) {
if (seteuid(0) == -1)
return (B_FALSE);
err = ea_open(&ef, file, "SunOS", EO_TAIL, O_RDONLY, 0);
if (seteuid(getuid()) == 1)
die(gettext("seteuid() failed"));
if (err == -1)
return (B_FALSE);
bzero(&eo, sizeof (eo));
if (ea_previous_object(&ef, &eo) == EO_ERROR) {
if (ea_error() != EXR_EOF) {
(void) ea_close(&ef);
return (B_FALSE);
}
} else {
uint_t c = eo.eo_catalog & EXD_DATA_MASK;
if (eo.eo_type != EO_GROUP ||
(eo.eo_catalog & EXC_CATALOG_MASK) != EXC_NONE ||
(!(c == EXD_GROUP_PROC && type == AC_PROC ||
c == EXD_GROUP_TASK && type == AC_TASK ||
c == EXD_GROUP_FLOW && type == AC_FLOW ||
(c == EXD_GROUP_NET_LINK_DESC ||
c == EXD_GROUP_NET_FLOW_DESC ||
c == EXD_GROUP_NET_LINK_STATS ||
c == EXD_GROUP_NET_FLOW_STATS) &&
type == AC_NET))) {
(void) ea_close(&ef);
return (B_FALSE);
}
}
(void) ea_close(&ef);
}
return (B_TRUE);
}