diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile --- a/share/man/man9/Makefile +++ b/share/man/man9/Makefile @@ -1028,6 +1028,7 @@ MLINKS+=dev_clone.9 drain_dev_clone_events.9 MLINKS+=dev_refthread.9 devvn_refthread.9 \ dev_refthread.9 dev_relthread.9 +MLINKS+=devctl_notify.9 devctl_notifyf.9 MLINKS+=devfs_set_cdevpriv.9 devfs_clear_cdevpriv.9 \ devfs_set_cdevpriv.9 devfs_get_cdevpriv.9 \ devfs_set_cdevpriv.9 devfs_foreach_cdevpriv.9 diff --git a/share/man/man9/devctl_notify.9 b/share/man/man9/devctl_notify.9 --- a/share/man/man9/devctl_notify.9 +++ b/share/man/man9/devctl_notify.9 @@ -21,16 +21,19 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd September 22, 2020 +.Dd April 21, 2025 .Dt DEVCTL_NOTIFY 9 .Os .Sh NAME -.Nm devctl_notify +.Nm devctl_notify , +.Nm devctl_notifyf .Nd Send a message, via devctl, to userland .Sh SYNOPSIS .In sys/devctl.h .Ft void .Fn devctl_notify "const char *system" "const char *subsystem" "const char *type" "const char *data" +.Ft void +.Fn devctl_notifyf "const char *system" "const char *subsystem" "const char *type" "const char *datafmt" "..." .Sh DESCRIPTION Send a notification to user land via .Xr devctl 4 . @@ -64,11 +67,22 @@ discover itself and sending all the data userland will want to use to decide what to do with the message. .Pp +The +.Fn devctl_notifyf +function is identical to +.Fn devctl_notify , +except it takes a format string to populate +.Fa data +and formats it as +.Xr printf 9 +would print it. +.Pp The current total message length limit is just under 1kb. Senders should try to remain well below this limit. .Sh SEE ALSO .Xr devctl 4 , -.Xr devd 8 +.Xr devd 8 , +.Xr printf 9 .Sh AUTHORS This manual page was written by .An M. Warner Losh diff --git a/sys/kern/kern_devctl.c b/sys/kern/kern_devctl.c --- a/sys/kern/kern_devctl.c +++ b/sys/kern/kern_devctl.c @@ -430,19 +430,20 @@ } /** - * @brief Send a 'notification' to userland, using standard ways + * @brief Send a 'notification' to userland, using standard ways. This version + * takes a format string and varargs, which fits a number of consumers' slightly + * less trivial needs. */ void -devctl_notify(const char *system, const char *subsystem, const char *type, - const char *data) +devctl_notifyf(const char *system, const char *subsystem, const char *type, + const char *datafmt, ...) { struct dev_event_info *dei; struct sbuf sb; + ssize_t metasz; if (system == NULL || subsystem == NULL || type == NULL) return; - if (devctl_notify_hook.send_f != NULL) - devctl_notify_hook.send_f(system, subsystem, type, data); dei = devctl_alloc_dei_sb(&sb); if (dei == NULL) return; @@ -452,15 +453,77 @@ sbuf_cat(&sb, subsystem); sbuf_cat(&sb, " type="); sbuf_cat(&sb, type); - if (data != NULL) { + + metasz = sbuf_len(&sb); + if (datafmt != NULL && *datafmt != '\0') { + va_list ap; + + va_start(ap, datafmt); sbuf_putc(&sb, ' '); - sbuf_cat(&sb, data); + sbuf_vprintf(&sb, datafmt, ap); + va_end(ap); } + sbuf_putc(&sb, '\n'); - if (sbuf_finish(&sb) != 0) + if (sbuf_finish(&sb) != 0) { devctl_free_dei(dei); /* overflow -> drop it */ - else - devctl_queue(dei); + return; + } + + if (devctl_notify_hook.send_f != NULL) { + ssize_t datasz; + char *hdata, *nlpos; + + MPASS(metasz > 0); /* Implies we overflowed */ + + nlpos = NULL; + if (datafmt == NULL) { + hdata = NULL; + } else { + /* + * Chop off the newline while we send it to the hook, + * we'll restore it after. + */ + hdata = sbuf_data(&sb); + datasz = sbuf_len(&sb); + nlpos = &hdata[datasz - 1]; + + MPASS(hdata[metasz] == ' '); + MPASS(*nlpos == '\n'); + *nlpos = '\0'; + + + /* + * The sbuf was laid out like: [metadata] data, + * so we advance here past the space. + */ + hdata += metasz + 1; + } + + devctl_notify_hook.send_f(system, subsystem, type, hdata); + + /* Fix the devctl formatted string before we enqueue. */ + if (nlpos != NULL) { + *nlpos = '\n'; + } + } + + devctl_queue(dei); +} + +/** + * @brief Send a 'notification' to userland, using standard ways + */ +void +devctl_notify(const char *system, const char *subsystem, const char *type, + const char *data) +{ + + if (data != NULL) { + devctl_notifyf(system, subsystem, type, "%s", data); + } else { + devctl_notifyf(system, subsystem, type, NULL); + } } /* diff --git a/sys/sys/devctl.h b/sys/sys/devctl.h --- a/sys/sys/devctl.h +++ b/sys/sys/devctl.h @@ -14,6 +14,8 @@ */ bool devctl_process_running(void); +void devctl_notifyf(const char *__system, const char *__subsystem, + const char *__type, const char *__datafmt, ...) __printflike(4, 5); void devctl_notify(const char *__system, const char *__subsystem, const char *__type, const char *__data); struct sbuf;