mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-27 11:55:06 +00:00
syslogd: Log messages using libcasper
Some logging operations require access to external resources to complete. Logging to F_WALL requires on-demand access to the user accounting database. Logging to F_CONSOLE requires access to the console. Logging to F_PIPE prompts execution of a command outside of capability mode. These operations cannot be performed in capability mode, so the "p_open", "ttymsg", and "wallmsg" commands may be sent to libcasper to circumvent these limitations. Reviewed by: markj Differential Revision: https://reviews.freebsd.org/D41465
This commit is contained in:
parent
2567168dc4
commit
61a29eca55
@ -15,7 +15,8 @@ LIBADD= util
|
||||
|
||||
.if ${MK_CASPER} != "no"
|
||||
SRCS+= syslogd_cap.c \
|
||||
syslogd_cap_config.c
|
||||
syslogd_cap_config.c \
|
||||
syslogd_cap_log.c
|
||||
CFLAGS+= -DWITH_CASPER
|
||||
LIBADD+= cap_net casper nv
|
||||
.endif
|
||||
|
@ -129,7 +129,6 @@
|
||||
#include "pathnames.h"
|
||||
#include "syslogd.h"
|
||||
#include "syslogd_cap.h"
|
||||
#include "ttymsg.h"
|
||||
|
||||
const char *ConfFile = _PATH_LOGCONF;
|
||||
static const char *PidFile = _PATH_LOGPID;
|
||||
@ -337,12 +336,10 @@ static int evaluate_prop_filter(const struct prop_filter *filter,
|
||||
static nvlist_t *prop_filter_compile(const char *);
|
||||
static void parsemsg(const char *, char *);
|
||||
static void printsys(char *);
|
||||
static int p_open(const char *, pid_t *);
|
||||
static const char *ttymsg_check(struct iovec *, int, char *, int);
|
||||
static void usage(void);
|
||||
static bool validate(struct sockaddr *, const char *);
|
||||
static void unmapped(struct sockaddr *);
|
||||
static void wallmsg(struct filed *, struct iovec *, const int iovlen);
|
||||
static int waitdaemon(int);
|
||||
static void increase_rcvbuf(int);
|
||||
|
||||
@ -1665,16 +1662,6 @@ dofsync(void)
|
||||
needdofsync = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* List of iovecs to which entries can be appended.
|
||||
* Used for constructing the message to be logged.
|
||||
*/
|
||||
struct iovlist {
|
||||
struct iovec iov[TTYMSG_IOV_MAX];
|
||||
size_t iovcnt;
|
||||
size_t totalsize;
|
||||
};
|
||||
|
||||
static void
|
||||
iovlist_init(struct iovlist *il)
|
||||
{
|
||||
@ -1831,8 +1818,16 @@ fprintlog_write(struct filed *f, struct iovlist *il, int flags)
|
||||
dprintf(" %s\n", f->f_pname);
|
||||
iovlist_append(il, "\n");
|
||||
if (f->f_procdesc == -1) {
|
||||
if ((f->f_file = p_open(f->f_pname,
|
||||
&f->f_procdesc)) < 0) {
|
||||
struct filed *f_in_list;
|
||||
size_t i = 0;
|
||||
|
||||
STAILQ_FOREACH(f_in_list, &fhead, next) {
|
||||
if (f_in_list == f)
|
||||
break;
|
||||
++i;
|
||||
}
|
||||
f->f_file = p_open(i, f->f_pname, &f->f_procdesc);
|
||||
if (f->f_file < 0) {
|
||||
logerror(f->f_pname);
|
||||
break;
|
||||
}
|
||||
@ -2073,9 +2068,12 @@ fprintlog_successive(struct filed *f, int flags)
|
||||
*
|
||||
* Write the specified message to either the entire
|
||||
* world, or a list of approved users.
|
||||
*
|
||||
* Note: This function is wrapped by cap_wallmsg() when Capsicum support is
|
||||
* enabled so ttymsg() can be called.
|
||||
*/
|
||||
static void
|
||||
wallmsg(struct filed *f, struct iovec *iov, const int iovlen)
|
||||
void
|
||||
wallmsg(const struct filed *f, struct iovec *iov, const int iovlen)
|
||||
{
|
||||
static int reenter; /* avoid calling ourselves */
|
||||
struct utmpx *ut;
|
||||
@ -2091,10 +2089,8 @@ wallmsg(struct filed *f, struct iovec *iov, const int iovlen)
|
||||
continue;
|
||||
if (f->f_type == F_WALL) {
|
||||
if ((p = ttymsg(iov, iovlen, ut->ut_line,
|
||||
TTYMSGTIME)) != NULL) {
|
||||
errno = 0; /* already in msg */
|
||||
logerror(p);
|
||||
}
|
||||
TTYMSGTIME)) != NULL)
|
||||
dprintf("%s\n", p);
|
||||
continue;
|
||||
}
|
||||
/* should we send the message to this user? */
|
||||
@ -2103,10 +2099,8 @@ wallmsg(struct filed *f, struct iovec *iov, const int iovlen)
|
||||
break;
|
||||
if (!strcmp(f->f_uname[i], ut->ut_user)) {
|
||||
if ((p = ttymsg_check(iov, iovlen, ut->ut_line,
|
||||
TTYMSGTIME)) != NULL) {
|
||||
errno = 0; /* already in msg */
|
||||
logerror(p);
|
||||
}
|
||||
TTYMSGTIME)) != NULL)
|
||||
dprintf("%s\n", p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -2392,6 +2386,13 @@ parseconfigfile(FILE *cf, bool allow_includes, nvlist_t *nvl_conf)
|
||||
return (nvl_conf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read configuration file and create filed entries for each line.
|
||||
*
|
||||
* Note: This function is wrapped by cap_readconfigfile() when Capsicum
|
||||
* support is enabled so resources can be acquired outside of the security
|
||||
* sandbox.
|
||||
*/
|
||||
nvlist_t *
|
||||
readconfigfile(const char *path)
|
||||
{
|
||||
@ -3433,15 +3434,18 @@ validate(struct sockaddr *sa, const char *hname)
|
||||
/*
|
||||
* Fairly similar to popen(3), but returns an open descriptor, as
|
||||
* opposed to a FILE *.
|
||||
*
|
||||
* Note: This function is wrapped by cap_p_open() when Capsicum support is
|
||||
* enabled, which allows piped processes to run outside of the capability
|
||||
* sandbox.
|
||||
*/
|
||||
static int
|
||||
int
|
||||
p_open(const char *prog, int *rpd)
|
||||
{
|
||||
struct sigaction act = { };
|
||||
int pfd[2], pd;
|
||||
pid_t pid;
|
||||
char *argv[4]; /* sh -c cmd NULL */
|
||||
char errmsg[200];
|
||||
|
||||
if (pipe(pfd) == -1)
|
||||
return (-1);
|
||||
@ -3456,18 +3460,14 @@ p_open(const char *prog, int *rpd)
|
||||
argv[1] = strdup("-c");
|
||||
argv[2] = strdup(prog);
|
||||
argv[3] = NULL;
|
||||
if (argv[0] == NULL || argv[1] == NULL || argv[2] == NULL) {
|
||||
logerror("strdup");
|
||||
exit(1);
|
||||
}
|
||||
if (argv[0] == NULL || argv[1] == NULL || argv[2] == NULL)
|
||||
err(1, "strdup");
|
||||
|
||||
alarm(0);
|
||||
act.sa_handler = SIG_DFL;
|
||||
for (size_t i = 0; i < nitems(sigcatch); ++i) {
|
||||
if (sigaction(sigcatch[i], &act, NULL) == -1) {
|
||||
logerror("sigaction");
|
||||
exit(1);
|
||||
}
|
||||
if (sigaction(sigcatch[i], &act, NULL) == -1)
|
||||
err(1, "sigaction");
|
||||
}
|
||||
|
||||
dup2(pfd[0], STDIN_FILENO);
|
||||
@ -3490,11 +3490,8 @@ p_open(const char *prog, int *rpd)
|
||||
*/
|
||||
if (fcntl(pfd[1], F_SETFL, O_NONBLOCK) == -1) {
|
||||
/* This is bad. */
|
||||
(void)snprintf(errmsg, sizeof(errmsg),
|
||||
"Warning: cannot change pipe to PID %d to "
|
||||
"non-blocking behaviour.",
|
||||
(int)pid);
|
||||
logerror(errmsg);
|
||||
dprintf("Warning: cannot change pipe to PID %d to non-blocking"
|
||||
"behaviour.", pid);
|
||||
}
|
||||
*rpd = pd;
|
||||
return (pfd[1]);
|
||||
|
@ -67,6 +67,7 @@
|
||||
#include <sys/nv.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#define SYSLOG_NAMES
|
||||
#include <sys/syslog.h>
|
||||
@ -75,6 +76,8 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "ttymsg.h"
|
||||
|
||||
#define MAXLINE 8192 /* maximum line length */
|
||||
#define MAXSVLINE MAXLINE /* maximum saved line length */
|
||||
#define MAXUNAMES 20 /* maximum number of user names */
|
||||
@ -172,11 +175,23 @@ struct filed {
|
||||
STAILQ_ENTRY(filed) next; /* next in linked list */
|
||||
};
|
||||
|
||||
/*
|
||||
* List of iovecs to which entries can be appended.
|
||||
* Used for constructing the message to be logged.
|
||||
*/
|
||||
struct iovlist {
|
||||
struct iovec iov[TTYMSG_IOV_MAX];
|
||||
size_t iovcnt;
|
||||
size_t totalsize;
|
||||
};
|
||||
|
||||
extern const char *ConfFile;
|
||||
extern char LocalHostName[MAXHOSTNAMELEN];
|
||||
|
||||
void closelogfiles(void);
|
||||
void logerror(const char *);
|
||||
int p_open(const char *, pid_t *);
|
||||
nvlist_t *readconfigfile(const char *);
|
||||
void wallmsg(const struct filed *, struct iovec *, const int);
|
||||
|
||||
#endif /* !_SYSLOGD_H_ */
|
||||
|
@ -45,8 +45,14 @@ casper_command(const char *cmd, const nvlist_t *limits __unused,
|
||||
{
|
||||
int error = EINVAL;
|
||||
|
||||
if (strcmp(cmd, "readconfigfile") == 0)
|
||||
if (strcmp(cmd, "p_open") == 0)
|
||||
error = casper_p_open(nvlin, nvlout);
|
||||
else if (strcmp(cmd, "readconfigfile") == 0)
|
||||
error = casper_readconfigfile(nvlin, nvlout);
|
||||
else if (strcmp(cmd, "ttymsg") == 0)
|
||||
error = casper_ttymsg(nvlin, nvlout);
|
||||
else if (strcmp(cmd, "wallmsg") == 0)
|
||||
error = casper_wallmsg(nvlin);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
@ -47,8 +47,27 @@
|
||||
|
||||
#include "syslogd.h"
|
||||
|
||||
/*
|
||||
* Information used to verify filed integrity when executing outside of the
|
||||
* security sandbox.
|
||||
*/
|
||||
struct cap_filed {
|
||||
size_t idx;
|
||||
char pipe_cmd[MAXPATHLEN];
|
||||
SLIST_ENTRY(cap_filed) next;
|
||||
};
|
||||
extern SLIST_HEAD(cfiled_list, cap_filed) cfiled_head;
|
||||
|
||||
int cap_p_open(cap_channel_t *, size_t, const char *, int *);
|
||||
nvlist_t *cap_readconfigfile(cap_channel_t *, const char *);
|
||||
const char *cap_ttymsg(cap_channel_t *, struct iovec *, int, const char *, int);
|
||||
void cap_wallmsg(cap_channel_t *, const struct filed *, struct iovec *,
|
||||
const int);
|
||||
|
||||
int casper_p_open(nvlist_t *, nvlist_t *);
|
||||
int casper_readconfigfile(nvlist_t *, nvlist_t *);
|
||||
int casper_ttymsg(nvlist_t *, nvlist_t *);
|
||||
int casper_wallmsg(nvlist_t *);
|
||||
|
||||
nvlist_t *filed_to_nvlist(const struct filed *);
|
||||
nvlist_t *prop_filter_to_nvlist(const struct prop_filter *pfilter);
|
||||
@ -58,8 +77,14 @@ struct prop_filter *nvlist_to_prop_filter(const nvlist_t *nvl_prop_filter);
|
||||
|
||||
#else /* !WITH_CASPER */
|
||||
|
||||
#define cap_p_open(chan, f_idx, prog, rpd) \
|
||||
p_open(prog, rpd)
|
||||
#define cap_readconfigfile(chan, cf) \
|
||||
readconfigfile(cf)
|
||||
#define cap_ttymsg(chan, iov, iovcnt, line, tmout) \
|
||||
ttymsg(iov, iovcnt, line, tmout)
|
||||
#define cap_wallmsg(chan, f, iov, iovcnt) \
|
||||
wallmsg(f, iov, iovcnt)
|
||||
|
||||
#endif /* WITH_CASPER */
|
||||
|
||||
|
@ -277,6 +277,9 @@ cap_readconfigfile(cap_channel_t *chan, const char *path)
|
||||
int
|
||||
casper_readconfigfile(nvlist_t *nvlin, nvlist_t *nvlout)
|
||||
{
|
||||
const nvlist_t * const *filed_list;
|
||||
nvlist_t *nvl_conf;
|
||||
size_t n_fileds;
|
||||
const char *path;
|
||||
|
||||
/*
|
||||
@ -291,6 +294,35 @@ casper_readconfigfile(nvlist_t *nvlin, nvlist_t *nvlout)
|
||||
strlcpy(LocalHostName, nvlist_get_string(nvlin, "LocalHostName"),
|
||||
sizeof(LocalHostName));
|
||||
|
||||
nvlist_move_nvlist(nvlout, "nvl_conf", readconfigfile(path));
|
||||
nvl_conf = readconfigfile(path);
|
||||
|
||||
/* Remove old filed data in case we are reloading. */
|
||||
while (!SLIST_EMPTY(&cfiled_head)) {
|
||||
struct cap_filed *cfiled;
|
||||
|
||||
cfiled = SLIST_FIRST(&cfiled_head);
|
||||
SLIST_REMOVE_HEAD(&cfiled_head, next);
|
||||
free(cfiled);
|
||||
}
|
||||
/* Record F_PIPE filed data for use in p_open(). */
|
||||
if (!nvlist_exists_nvlist_array(nvl_conf, "filed_list"))
|
||||
return (0);
|
||||
filed_list = nvlist_get_nvlist_array(nvl_conf, "filed_list", &n_fileds);
|
||||
for (size_t i = 0; i < n_fileds; ++i) {
|
||||
if (nvlist_get_number(filed_list[i], "f_type") == F_PIPE) {
|
||||
struct cap_filed *cfiled;
|
||||
const char *pipe_cmd;
|
||||
|
||||
cfiled = malloc(sizeof(*cfiled));
|
||||
if (cfiled == NULL)
|
||||
err(1, "malloc");
|
||||
cfiled->idx = i;
|
||||
pipe_cmd = nvlist_get_string(filed_list[i], "f_pname");
|
||||
strlcpy(cfiled->pipe_cmd, pipe_cmd, sizeof(cfiled->pipe_cmd));
|
||||
SLIST_INSERT_HEAD(&cfiled_head, cfiled, next);
|
||||
}
|
||||
}
|
||||
|
||||
nvlist_move_nvlist(nvlout, "nvl_conf", nvl_conf);
|
||||
return (0);
|
||||
}
|
||||
|
211
usr.sbin/syslogd/syslogd_cap_log.c
Normal file
211
usr.sbin/syslogd/syslogd_cap_log.c
Normal file
@ -0,0 +1,211 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2023 The FreeBSD Foundation
|
||||
*
|
||||
* This software was developed by Jake Freeland <jfree@FreeBSD.org>
|
||||
* under sponsorship from the FreeBSD Foundation.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <err.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "syslogd_cap.h"
|
||||
|
||||
struct cfiled_list cfiled_head;
|
||||
|
||||
int
|
||||
cap_p_open(cap_channel_t *chan, size_t filed_idx, const char *prog,
|
||||
int *procdesc)
|
||||
{
|
||||
nvlist_t *nvl = nvlist_create(0);
|
||||
int error, pipedesc_w;
|
||||
|
||||
nvlist_add_string(nvl, "cmd", "p_open");
|
||||
nvlist_add_number(nvl, "filed_idx", filed_idx);
|
||||
nvlist_add_string(nvl, "prog", prog);
|
||||
nvl = cap_xfer_nvlist(chan, nvl);
|
||||
if (nvl == NULL) {
|
||||
logerror("Failed to xfer p_open nvlist");
|
||||
exit(1);
|
||||
}
|
||||
error = nvlist_get_number(nvl, "error");
|
||||
if (error != 0) {
|
||||
errno = error;
|
||||
logerror("Failed to open piped command");
|
||||
}
|
||||
pipedesc_w = dnvlist_take_descriptor(nvl, "pipedesc_w", -1);
|
||||
*procdesc = dnvlist_take_descriptor(nvl, "procdesc", -1);
|
||||
|
||||
nvlist_destroy(nvl);
|
||||
return (pipedesc_w);
|
||||
}
|
||||
|
||||
int
|
||||
casper_p_open(nvlist_t *nvlin, nvlist_t *nvlout)
|
||||
{
|
||||
struct cap_filed *cfiled;
|
||||
size_t filed_idx;
|
||||
int pipedesc_w, procdesc = -1;
|
||||
const char *prog;
|
||||
|
||||
filed_idx = nvlist_get_number(nvlin, "filed_idx");
|
||||
prog = nvlist_get_string(nvlin, "prog");
|
||||
SLIST_FOREACH(cfiled, &cfiled_head, next) {
|
||||
if (cfiled->idx != filed_idx)
|
||||
continue;
|
||||
if (strcmp(cfiled->pipe_cmd, prog) != 0)
|
||||
return (-1);
|
||||
|
||||
pipedesc_w = p_open(prog, &procdesc);
|
||||
if (pipedesc_w == -1)
|
||||
return (-1);
|
||||
nvlist_move_descriptor(nvlout, "pipedesc_w", pipedesc_w);
|
||||
nvlist_move_descriptor(nvlout, "procdesc", procdesc);
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
const char *
|
||||
cap_ttymsg(cap_channel_t *chan, struct iovec *iov, int iovcnt,
|
||||
const char *line, int tmout)
|
||||
{
|
||||
nvlist_t *nvl = nvlist_create(0);
|
||||
int error;
|
||||
static char errbuf[1024];
|
||||
char *ret = NULL;
|
||||
|
||||
nvlist_add_string(nvl, "cmd", "ttymsg");
|
||||
for (int i = 0; i < iovcnt; ++i)
|
||||
nvlist_append_string_array(nvl, "iov_strs", iov[i].iov_base);
|
||||
nvlist_add_string(nvl, "line", line);
|
||||
nvlist_add_number(nvl, "tmout", tmout);
|
||||
|
||||
nvl = cap_xfer_nvlist(chan, nvl);
|
||||
if (nvl == NULL) {
|
||||
logerror("Failed to xfer ttymsg nvlist");
|
||||
exit(1);
|
||||
}
|
||||
error = nvlist_get_number(nvl, "error");
|
||||
if (error != 0) {
|
||||
errno = error;
|
||||
logerror("Failed to ttymsg");
|
||||
}
|
||||
if (nvlist_exists_string(nvl, "errstr")) {
|
||||
const char *errstr = nvlist_get_string(nvl, "errstr");
|
||||
(void)strlcpy(errbuf, errstr, sizeof(errbuf));
|
||||
ret = errbuf;
|
||||
}
|
||||
|
||||
nvlist_destroy(nvl);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
int
|
||||
casper_ttymsg(nvlist_t *nvlin, nvlist_t *nvlout)
|
||||
{
|
||||
char **nvlstrs;
|
||||
struct iovec *iov;
|
||||
size_t iovcnt;
|
||||
int tmout;
|
||||
const char *line;
|
||||
|
||||
nvlstrs = nvlist_take_string_array(nvlin, "iov_strs", &iovcnt);
|
||||
assert(iovcnt <= TTYMSG_IOV_MAX);
|
||||
iov = calloc(iovcnt, sizeof(*iov));
|
||||
if (iov == NULL)
|
||||
err(EXIT_FAILURE, "calloc");
|
||||
for (size_t i = 0; i < iovcnt; ++i) {
|
||||
iov[i].iov_base = nvlstrs[i];
|
||||
iov[i].iov_len = strlen(nvlstrs[i]);
|
||||
}
|
||||
line = nvlist_get_string(nvlin, "line");
|
||||
tmout = nvlist_get_number(nvlin, "tmout");
|
||||
line = ttymsg(iov, iovcnt, line, tmout);
|
||||
if (line != NULL)
|
||||
nvlist_add_string(nvlout, "errstr", line);
|
||||
|
||||
free(iov);
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
cap_wallmsg(cap_channel_t *chan, const struct filed *f, struct iovec *iov,
|
||||
int iovcnt)
|
||||
{
|
||||
nvlist_t *nvl = nvlist_create(0);
|
||||
int error;
|
||||
|
||||
nvlist_add_string(nvl, "cmd", "wallmsg");
|
||||
/*
|
||||
* The filed_to_nvlist() function is not needed
|
||||
* here because wallmsg() only uses f_type and
|
||||
* fu_uname members, which are both inline.
|
||||
*/
|
||||
nvlist_add_binary(nvl, "filed", f, sizeof(*f));
|
||||
for (int i = 0; i < iovcnt; ++i)
|
||||
nvlist_append_string_array(nvl, "iov_strs", iov[i].iov_base);
|
||||
|
||||
nvl = cap_xfer_nvlist(chan, nvl);
|
||||
if (nvl == NULL) {
|
||||
logerror("Failed to xfer wallmsg nvlist");
|
||||
exit(1);
|
||||
}
|
||||
error = nvlist_get_number(nvl, "error");
|
||||
if (error != 0) {
|
||||
errno = error;
|
||||
logerror("Failed to wallmsg");
|
||||
}
|
||||
nvlist_destroy(nvl);
|
||||
}
|
||||
|
||||
int
|
||||
casper_wallmsg(nvlist_t *nvlin)
|
||||
{
|
||||
const struct filed *f;
|
||||
char **nvlstrs;
|
||||
struct iovec *iov;
|
||||
size_t sz;
|
||||
|
||||
f = nvlist_get_binary(nvlin, "filed", &sz);
|
||||
assert(sz == sizeof(*f));
|
||||
nvlstrs = nvlist_take_string_array(nvlin, "iov_strs", &sz);
|
||||
assert(sz <= TTYMSG_IOV_MAX);
|
||||
iov = calloc(sz, sizeof(*iov));
|
||||
if (iov == NULL)
|
||||
err(EXIT_FAILURE, "calloc");
|
||||
for (size_t i = 0; i < sz; ++i) {
|
||||
iov[i].iov_base = nvlstrs[i];
|
||||
iov[i].iov_len = strlen(nvlstrs[i]);
|
||||
}
|
||||
wallmsg(f, iov, sz);
|
||||
|
||||
for (size_t i = 0; i < sz; ++i)
|
||||
free(iov[i].iov_base);
|
||||
free(iov);
|
||||
return (0);
|
||||
}
|
Loading…
Reference in New Issue
Block a user