Changeset View
Standalone View
sbin/devd/devd.cc
Show First 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | |||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/socket.h> | #include <sys/socket.h> | ||||
#include <sys/stat.h> | #include <sys/stat.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/types.h> | #include <sys/types.h> | ||||
#include <sys/wait.h> | #include <sys/wait.h> | ||||
#include <sys/un.h> | #include <sys/un.h> | ||||
#include <sys/time.h> | |||||
#include <cctype> | #include <cctype> | ||||
#include <cerrno> | #include <cerrno> | ||||
#include <cstdlib> | #include <cstdlib> | ||||
#include <cstdio> | #include <cstdio> | ||||
#include <csignal> | #include <csignal> | ||||
#include <cstring> | #include <cstring> | ||||
#include <cstdarg> | #include <cstdarg> | ||||
#include <dirent.h> | #include <dirent.h> | ||||
#include <err.h> | #include <err.h> | ||||
#include <fcntl.h> | #include <fcntl.h> | ||||
#include <libutil.h> | #include <libutil.h> | ||||
#include <paths.h> | #include <paths.h> | ||||
#include <poll.h> | #include <poll.h> | ||||
#include <regex.h> | #include <regex.h> | ||||
#include <syslog.h> | #include <syslog.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
#include <stdint.h> | |||||
#include <algorithm> | #include <algorithm> | ||||
#include <map> | #include <map> | ||||
#include <string> | #include <string> | ||||
#include <list> | #include <list> | ||||
#include <stdexcept> | #include <stdexcept> | ||||
#include <vector> | #include <vector> | ||||
#include <exception> | |||||
#include "devd.h" /* C compatible definitions */ | #include "devd.h" /* C compatible definitions */ | ||||
#include "devd.hh" /* C++ class definitions */ | #include "devd.hh" /* C++ class definitions */ | ||||
#define STREAMPIPE "/var/run/devd.pipe" | #define STREAMPIPE "/var/run/devd.pipe" | ||||
#define SEQPACKETPIPE "/var/run/devd.seqpacket.pipe" | #define SEQPACKETPIPE "/var/run/devd.seqpacket.pipe" | ||||
#define CF "/etc/devd.conf" | #define CF "/etc/devd.conf" | ||||
#define SYSCTL "hw.bus.devctl_queue" | #define SYSCTL "hw.bus.devctl_queue" | ||||
▲ Show 20 Lines • Show All 735 Lines • ▼ Show 20 Lines | config::find_and_execute(char type) | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
process_event(char *buffer) | process_event(char *buffer) | ||||
{ | { | ||||
// TODO | |||||
// 1. Create "newerthan" syntax element for devd.conf | |||||
// 2. Make custom exception for redundant events | |||||
char type; | char type; | ||||
char *sp; | char *sp; | ||||
struct timeval tv; | struct timeval tv; | ||||
char *timestr; | char *timestr; | ||||
struct stat file_info; | |||||
gettimeofday(&tv, NULL); | |||||
sp = buffer + 1; | sp = buffer + 1; | ||||
devdlog(LOG_INFO, "Processing event '%s'\n", buffer); | |||||
type = *buffer++; | type = *buffer++; | ||||
const char* devmatch_ran = "/var/run/devmatch_ran"; | |||||
stat(devmatch_ran, &file_info); // Retrieves last modification timestamp of file | |||||
if ((tv.tv_sec < file_info.st_mtim.tv_sec || tv.tv_usec < file_info.st_mtim.tv_nsec / 1000) && type==nomatch) { // Checks if event is older than devmatch_ran | |||||
cperciva: This seems to be comparing the timestamp on the file to the *current* time. We want to compare… | |||||
impUnsubmitted Not Done Inline ActionsThe events from the kernel don't have timestamps. We add time= in devd, and by then it's too late. imp: The events from the kernel don't have timestamps. We add time= in devd, and by then it's too… | |||||
impUnsubmitted Not Done Inline ActionsAlso, you don't know that this is a valid test: you are still racing devmatch if you add timestamps to the events. The problem is that devmatch looks at the state of the system at a given time. There's no guarantee noting that time will help you filter events that happen between when you take the snapshot and when you note the time. Also, it bakes in that all events aren't so good... but most events are good. The only real way to fix this is to move devmatch into devd. Anything else is too racy. imp: Also, you don't know that this is a valid test: you are still racing devmatch if you add… | |||||
impUnsubmitted Not Done Inline ActionsAlso, this is early boot, ntpd might step the time. imp: Also, this is early boot, ntpd might step the time. | |||||
cpercivaUnsubmitted Not Done Inline Actions
I was expecting that we would run this before ntpd starts. But yes we need to be aware of that. cperciva: > Also, this is early boot, ntpd might step the time.
I was expecting that we would run this… | |||||
impUnsubmitted Not Done Inline ActionsIf time steps asynchronously this can't work. Ntpd does that at some random time after it starts. Rather than prevent, you are much much better off moving devmatch into devd. It's unclear to me if you need to process all the events before calling daemon or if you can defer the module stuff to a thread.... imp: If time steps asynchronously this can't work. Ntpd does that at some random time after it… | |||||
cpercivaUnsubmitted Not Done Inline Actions
The idea is that we're going to add a timestamp, that's a separate patch. cperciva: > The events from the kernel don't have timestamps. We add time= in devd, and by then it's too… | |||||
impUnsubmitted Not Done Inline ActionsIt's still too racey even with that as i explained. And it's solving the problem wrong. The whole issue is we reparse linker.hints for every fork. If devmatch moved entirely into devd we would onlybparse linker hints once and we'd retain knowledge of what we've loaded and we'd know we had all the events to do the freeze thaw properly and more often when we get bursts of new devices. imp: It's still too racey even with that as i explained.
And it's solving the problem wrong. The… | |||||
throw std::exception(); | |||||
cpercivaUnsubmitted Not Done Inline ActionsWhy not just return;? cperciva: Why not just `return;`? | |||||
iciliaat_gmail.comAuthorUnsubmitted Not Done Inline ActionsI decided to use an exception because I wanted it to be caught by the function calling it, so that I can then log the incident with devdlog. This allows for visibility into what's going on in the system. iciliaat_gmail.com: I decided to use an exception because I wanted it to be caught by the function calling it, so… | |||||
} | |||||
else { | |||||
devdlog(LOG_INFO, "Processing event '%s'\n", buffer); | |||||
cfg.push_var_table(); | cfg.push_var_table(); | ||||
// $* is the entire line | // $* is the entire line | ||||
cfg.set_variable("*", buffer - 1); | cfg.set_variable("*", buffer - 1); | ||||
// $_ is the entire line without the initial character | // $_ is the entire line without the initial character | ||||
cfg.set_variable("_", buffer); | cfg.set_variable("_", buffer); | ||||
// Save the time this happened (as approximated by when we got | // Save the time this happened (as approximated by when we got | ||||
// around to processing it). | // around to processing it). | ||||
gettimeofday(&tv, NULL); | |||||
asprintf(×tr, "%jd.%06ld", (uintmax_t)tv.tv_sec, tv.tv_usec); | asprintf(×tr, "%jd.%06ld", (uintmax_t)tv.tv_sec, tv.tv_usec); | ||||
cfg.set_variable("timestamp", timestr); | cfg.set_variable("timestamp", timestr); | ||||
free(timestr); | free(timestr); | ||||
// Match doesn't have a device, and the format is a little | // Match doesn't have a device, and the format is a little | ||||
// different, so handle it separately. | // different, so handle it separately. | ||||
switch (type) { | switch (type) { | ||||
case notify: | case notify: | ||||
//! (k=v)* | //! (k=v)* | ||||
sp = cfg.set_vars(sp); | sp = cfg.set_vars(sp); | ||||
break; | break; | ||||
case nomatch: | case nomatch: | ||||
//? at location pnp-info on bus | //? at location pnp-info on bus | ||||
sp = strchr(sp, ' '); | sp = strchr(sp, ' '); | ||||
if (sp == NULL) | if (sp == NULL) | ||||
return; /* Can't happen? */ | return; /* Can't happen? */ | ||||
*sp++ = '\0'; | *sp++ = '\0'; | ||||
while (isspace(*sp)) | while (isspace(*sp)) | ||||
sp++; | sp++; | ||||
if (strncmp(sp, "at ", 3) == 0) | if (strncmp(sp, "at ", 3) == 0) | ||||
sp += 3; | sp += 3; | ||||
sp = cfg.set_vars(sp); | sp = cfg.set_vars(sp); | ||||
while (isspace(*sp)) | while (isspace(*sp)) | ||||
sp++; | sp++; | ||||
if (strncmp(sp, "on ", 3) == 0) | if (strncmp(sp, "on ", 3) == 0) | ||||
cfg.set_variable("bus", sp + 3); | cfg.set_variable("bus", sp + 3); | ||||
break; | break; | ||||
case attach: /*FALLTHROUGH*/ | case attach: /*FALLTHROUGH*/ | ||||
case detach: | case detach: | ||||
sp = strchr(sp, ' '); | sp = strchr(sp, ' '); | ||||
if (sp == NULL) | if (sp == NULL) | ||||
return; /* Can't happen? */ | return; /* Can't happen? */ | ||||
*sp++ = '\0'; | *sp++ = '\0'; | ||||
cfg.set_variable("device-name", buffer); | cfg.set_variable("device-name", buffer); | ||||
while (isspace(*sp)) | while (isspace(*sp)) | ||||
sp++; | sp++; | ||||
if (strncmp(sp, "at ", 3) == 0) | if (strncmp(sp, "at ", 3) == 0) | ||||
sp += 3; | sp += 3; | ||||
sp = cfg.set_vars(sp); | sp = cfg.set_vars(sp); | ||||
while (isspace(*sp)) | while (isspace(*sp)) | ||||
sp++; | sp++; | ||||
if (strncmp(sp, "on ", 3) == 0) | if (strncmp(sp, "on ", 3) == 0) | ||||
cfg.set_variable("bus", sp + 3); | cfg.set_variable("bus", sp + 3); | ||||
break; | break; | ||||
} | } | ||||
cfg.find_and_execute(type); | cfg.find_and_execute(type); | ||||
cfg.pop_var_table(); | cfg.pop_var_table(); | ||||
} | } | ||||
} | |||||
static int | static int | ||||
create_socket(const char *name, int socktype) | create_socket(const char *name, int socktype) | ||||
{ | { | ||||
int fd, slen; | int fd, slen; | ||||
struct sockaddr_un sun; | struct sockaddr_un sun; | ||||
if ((fd = socket(PF_LOCAL, socktype, 0)) < 0) | if ((fd = socket(PF_LOCAL, socktype, 0)) < 0) | ||||
▲ Show 20 Lines • Show All 104 Lines • ▼ Show 20 Lines | if (s.fd != -1) { | ||||
++num_clients; | ++num_clients; | ||||
} else | } else | ||||
err(1, "accept"); | err(1, "accept"); | ||||
} | } | ||||
static void | static void | ||||
event_loop(void) | event_loop(void) | ||||
{ | { | ||||
cpercivaUnsubmitted Not Done Inline ActionsUnnecessary blank line. cperciva: Unnecessary blank line. | |||||
int rv; | int rv; | ||||
int fd; | int fd; | ||||
char buffer[DEVCTL_MAXBUF]; | char buffer[DEVCTL_MAXBUF]; | ||||
int once = 0; | int once = 0; | ||||
int stream_fd, seqpacket_fd, max_fd; | int stream_fd, seqpacket_fd, max_fd; | ||||
int accepting; | int accepting; | ||||
timeval tv; | timeval tv; | ||||
fd_set fds; | fd_set fds; | ||||
▲ Show 20 Lines • Show All 73 Lines • ▼ Show 20 Lines | if (FD_ISSET(fd, &fds)) { | ||||
"available event data exceeded " | "available event data exceeded " | ||||
"buffer space\n"); | "buffer space\n"); | ||||
} | } | ||||
notify_clients(buffer, rv); | notify_clients(buffer, rv); | ||||
buffer[rv] = '\0'; | buffer[rv] = '\0'; | ||||
while (buffer[--rv] == '\n') | while (buffer[--rv] == '\n') | ||||
buffer[rv] = '\0'; | buffer[rv] = '\0'; | ||||
try { | try { | ||||
process_event(buffer); | process_event(buffer); | ||||
} | } | ||||
// Include a second catch statement for late timestamps, so make process_event throw an exception | |||||
catch (const std::length_error& e) { | catch (const std::length_error& e) { | ||||
devdlog(LOG_ERR, "Dropping event %s " | devdlog(LOG_ERR, "Dropping event %s " | ||||
"due to low memory", buffer); | "due to low memory", buffer); | ||||
} | |||||
catch (const std::exception& e) { | |||||
devdlog(LOG_ERR, "Event %s is redundant", buffer); | |||||
} | } | ||||
} else if (rv < 0) { | } else if (rv < 0) { | ||||
if (errno != EINTR) | if (errno != EINTR) | ||||
break; | break; | ||||
} else { | } else { | ||||
/* EOF */ | /* EOF */ | ||||
break; | break; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 226 Lines • Show Last 20 Lines |
This seems to be comparing the timestamp on the file to the *current* time. We want to compare it to the time the event was *generated* (which is why we added the time=foo field to events).