#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ctfs.h>
#include <sys/contract/process.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <libcontract.h>
#include <libcontract_priv.h>
#include <libuutil.h>
#include <poll.h>
#include <port.h>
#include <signal.h>
#include <sys/wait.h>
#include <stdarg.h>
#include <locale.h>
#include <langinfo.h>
struct {
const char *name;
int found;
} types[] = {
{ "process", 0 },
{ "device", 0 },
{ NULL }
};
typedef struct watched_fd {
int wf_fd;
int wf_type;
} watched_fd_t;
static void
usage(void)
{
(void) fprintf(stderr, gettext(
"Usage: %s [-f] [-r] [-v] contract-id | contract-type ...\n"),
uu_getpname());
exit(UU_EXIT_USAGE);
}
static int
sopen(const char *format, const char *error, const char *permerror, ...)
{
char path[PATH_MAX];
int fd;
va_list varg;
va_start(varg, permerror);
if (vsnprintf(path, PATH_MAX, format, varg) >= PATH_MAX) {
errno = ENAMETOOLONG;
uu_vdie(error, varg);
}
if ((fd = open64(path, O_RDONLY | O_NONBLOCK)) == -1) {
if (permerror && (errno == EPERM || errno == EACCES))
uu_vwarn(permerror, varg);
else
uu_vdie(error, varg);
}
va_end(varg);
return (fd);
}
static void
hdr_event(void)
{
(void) printf("%-8s%-8s%-5s%-4s%-9s%s\n",
"CTID", "EVID", "CRIT", "ACK", "CTTYPE", "SUMMARY");
}
static int
get_event(int fd, int type, int verbose)
{
ct_evthdl_t ev;
uint_t flags;
if (errno = ct_event_read(fd, &ev)) {
if (errno == EAGAIN)
return (0);
uu_die(gettext("could not receive contract event"));
}
flags = ct_event_get_flags(ev);
(void) printf("%-8ld%-8lld%-5s%-4s%-9s",
ct_event_get_ctid(ev),
ct_event_get_evid(ev),
(flags & CTE_INFO) ? "info" : (flags & CTE_NEG) ? "neg" : "crit",
flags & CTE_ACK ? "yes" : "no",
types[type].name);
contract_event_dump(stdout, ev, verbose);
ct_event_free(ev);
return (1);
}
static int
get_type(const char *typestr)
{
int i;
for (i = 0; types[i].name; i++)
if (strcmp(types[i].name, typestr) == 0)
return (i);
uu_die(gettext("invalid contract type: %s\n"), typestr);
}
static int
contract_type(ctid_t id)
{
ct_stathdl_t hdl;
int type, fd;
if ((fd = contract_open(id, NULL, "status", O_RDONLY)) == -1)
return (-1);
if (errno = ct_status_read(fd, CTD_COMMON, &hdl)) {
(void) close(fd);
return (-1);
}
type = get_type(ct_status_get_type(hdl));
ct_status_free(hdl);
(void) close(fd);
return (type);
}
static int
ctid_compar(const void *a1, const void *a2)
{
ctid_t id1 = *(ctid_t *)a1;
ctid_t id2 = *(ctid_t *)a2;
if (id1 > id2)
return (1);
if (id2 > id1)
return (-1);
return (0);
}
int
main(int argc, char **argv)
{
int opt_reliable = 0;
int opt_reset = 0;
int opt_verbose = 0;
int port_fd;
watched_fd_t *wfd;
int i, nfds, nids;
ctid_t *ids, last;
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
(void) uu_setpname(argv[0]);
while ((i = getopt(argc, argv, "rfv")) != EOF) {
switch (i) {
case 'r':
opt_reliable = 1;
break;
case 'f':
opt_reset = 1;
break;
case 'v':
opt_verbose = 1;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc <= 0)
usage();
wfd = calloc(argc, sizeof (struct pollfd));
if (wfd == NULL)
uu_die("calloc");
ids = calloc(argc, sizeof (ctid_t));
if (ids == NULL)
uu_die("calloc");
nfds = 0;
nids = 0;
for (i = 0; i < argc; i++) {
int id;
if (strchr(argv[i], '/') != NULL)
uu_die(gettext("invalid contract type: %s\n"), argv[i]);
if (uu_strtoint(argv[i], &id, sizeof (id), 10, 1, INT_MAX)) {
int type;
wfd[nfds].wf_fd =
sopen(CTFS_ROOT "/%s/bundle",
gettext("invalid contract type: %s\n"), NULL,
argv[i]);
wfd[nfds].wf_type = type = get_type(argv[i]);
if (types[type].found) {
(void) close(wfd[nfds].wf_fd);
continue;
}
types[type].found = 1;
nfds++;
} else {
ids[nids++] = id;
}
}
qsort(ids, nids, sizeof (ctid_t), ctid_compar);
last = -1;
for (i = 0; i < nids; i++) {
int type, fd;
if (ids[i] == last)
continue;
last = ids[i];
fd = sopen(CTFS_ROOT "/all/%d/events",
gettext("invalid contract id: %d\n"),
gettext("could not access contract id %d\n"), ids[i]);
if (fd == -1)
continue;
if ((type = contract_type(ids[i])) == -1) {
(void) close(fd);
uu_warn(gettext("could not access contract id %d\n"),
ids[i]);
continue;
}
if (types[type].found) {
(void) close(fd);
continue;
}
wfd[nfds].wf_fd = fd;
wfd[nfds].wf_type = type;
nfds++;
}
free(ids);
if (nfds == 0)
uu_die(gettext("no contracts to watch\n"));
if (opt_reliable)
for (i = 0; i < nfds; i++)
if (ioctl(wfd[i].wf_fd, CT_ERELIABLE, NULL) == -1) {
uu_warn("could not request reliable events");
break;
}
if (opt_reset)
for (i = 0; i < nfds; i++)
(void) ioctl(wfd[i].wf_fd, CT_ERESET, NULL);
if ((port_fd = port_create()) == -1)
goto port_error;
for (i = 0; i < nfds; i++)
if (port_associate(port_fd, PORT_SOURCE_FD, wfd[i].wf_fd,
POLLIN, &wfd[i]) == -1)
goto port_error;
hdr_event();
for (;;) {
port_event_t pe;
watched_fd_t *w;
if (port_get(port_fd, &pe, NULL) == -1) {
if (errno == EINTR)
continue;
goto port_error;
}
w = pe.portev_user;
while (get_event(pe.portev_object, w->wf_type, opt_verbose))
;
if (port_associate(port_fd, PORT_SOURCE_FD, pe.portev_object,
POLLIN, pe.portev_user) == -1)
goto port_error;
}
port_error:
uu_die(gettext("error waiting for contract events"));
return (1);
}