node_modules | node_modules | ||||
udev/udev-monitor |
var syscheck = require("./js/syscheck"); | var syscheck = require("./js/syscheck"); | ||||
var parseConf = require("./js/parse-conf"); | var parseConf = require("./js/parse-conf"); | ||||
var async = require("./js/async"); | var async = require("./js/async"); | ||||
var udev = require("./udev"); | |||||
var modules = { | var modules = { | ||||
// display: require("./modules/display"), | // display: require("./modules/display"), | ||||
} | } | ||||
function onTerm() { | function onTerm() { | ||||
udev.exit(); | |||||
stopAll(() => process.exit(1)); | stopAll(() => process.exit(1)); | ||||
} | } | ||||
var udev = require("udev"); | |||||
var udev = require("../../udev"); | |||||
exports.start = start; | exports.start = start; | ||||
exports.stop = stop; | exports.stop = stop; | ||||
var logger; | var logger; | ||||
var modules; | var modules; | ||||
var monitor; | |||||
function onchange(dev, evt) { | |||||
if (evt === "add") | |||||
function onchange(dev) { | |||||
if (dev.ACTION === "add") | |||||
logger.info("display added"); | logger.info("display added"); | ||||
else if (evt === "change") | |||||
else if (dev.ACTION === "change") | |||||
logger.info("display changed"); | logger.info("display changed"); | ||||
else | else | ||||
logger.info(dev); | logger.info(dev); | ||||
logger = logger_ || logger; | logger = logger_ || logger; | ||||
modules = modules_ || modules; | modules = modules_ || modules; | ||||
monitor = udev.monitor("drm"); | |||||
monitor.on("add", dev => onchange(dev, "add")); | |||||
monitor.on("change", dev => onchange(dev, "change")); | |||||
udev.monitor("drm", onchange); | |||||
} | } | ||||
function stop(cb) { | function stop(cb) { | ||||
udev.unmonitor("drm", onchange); | |||||
cb(); | cb(); | ||||
} | } | ||||
var udev = require("udev"); | |||||
var udev = require("../../udev"); | |||||
var spawn = require("child_process").spawn; | var spawn = require("child_process").spawn; | ||||
var exec = require("child_process").exec; | var exec = require("child_process").exec; | ||||
var logger; | var logger; | ||||
var modules; | var modules; | ||||
var monitor; | |||||
// Set an xinput property | // Set an xinput property | ||||
function setProp(name, val, suppressWarnings) { | function setProp(name, val, suppressWarnings) { | ||||
var args = [ "--set-prop", name ]; | var args = [ "--set-prop", name ]; | ||||
return true; | return true; | ||||
} | } | ||||
function onchange(dev, evt) { | |||||
function onchange(dev) { | |||||
if (!filter(dev)) | if (!filter(dev)) | ||||
return; | return; | ||||
inputType = "mouse"; | inputType = "mouse"; | ||||
// Log add/change | // Log add/change | ||||
if (evt === "add") | |||||
if (dev.ACTION === "add") | |||||
logger.info(inputType, dev.NAME, "added"); | logger.info(inputType, dev.NAME, "added"); | ||||
else if (evt === "change") | |||||
else if (dev.ACTION === "change") | |||||
logger.info(inputType, dev.NAME, "changed"); | logger.info(inputType, dev.NAME, "changed"); | ||||
// Run through and apply relevant rules | // Run through and apply relevant rules | ||||
logger = logger_ || logger; | logger = logger_ || logger; | ||||
modules = modules_ || modules; | modules = modules_ || modules; | ||||
udev.list("input").forEach(dev => onchange(dev, "init")); | |||||
monitor = udev.monitor("input"); | |||||
monitor.on("add", dev => onchange(dev, "add")); | |||||
monitor.on("change", dev => onchange(dev, "change")); | |||||
udev.list("input", devs => devs.forEach(onchange)) | |||||
udev.monitor("input", onchange); | |||||
} | } | ||||
function stop(cb) { | function stop(cb) { | ||||
monitor.close(); | |||||
udev.unmonitor("input", onchange); | |||||
cb(); | cb(); | ||||
} | } | ||||
{ | { | ||||
"name": "dedaemon", | "name": "dedaemon", | ||||
"version": "0.0.1", | |||||
"version": "0.1.0", | |||||
"description": "", | "description": "", | ||||
"main": "daemon.js", | "main": "daemon.js", | ||||
"scripts": { | "scripts": { | ||||
"test": "echo \"Error: no test specified\" && exit 1" | |||||
"test": "echo \"Error: no test specified\" && exit 1", | |||||
"postinstall": "make -C udev" | |||||
}, | }, | ||||
"repository": { | "repository": { | ||||
"type": "git", | "type": "git", | ||||
"author": "Martin Dørum Nygaard <martid0311@gmail.com> (http://mort.coffee)", | "author": "Martin Dørum Nygaard <martid0311@gmail.com> (http://mort.coffee)", | ||||
"license": "ISC", | "license": "ISC", | ||||
"dependencies": { | "dependencies": { | ||||
"hconfig": "^0.4.0", | |||||
"udev": "^0.4.0" | |||||
"hconfig": "^0.4.0" | |||||
}, | }, | ||||
"bin": { | "bin": { | ||||
"dedaemon": "./dedaemon.js" | "dedaemon": "./dedaemon.js" |
TARGET=udev-monitor | |||||
SRC=src/main.c src/json.c | |||||
HDR=src/json.h | |||||
$(TARGET): $(SRC) $(HDR) | |||||
$(CC) -o $(TARGET) -g $(shell pkg-config --cflags --libs libudev) $(SRC) | |||||
clean: | |||||
rm -f $(TARGET) |
var spawn = require("child_process").spawn; | |||||
var child = spawn(__dirname+"/udev-monitor"); | |||||
exports.list = list; | |||||
exports.monitor = monitor; | |||||
exports.unmonitor = unmonitor; | |||||
exports.exit = exit; | |||||
var currstr = ""; | |||||
child.stdout.setEncoding("utf8"); | |||||
child.stdout.on("data", d => { | |||||
var lines = d.toString().split("\n"); | |||||
currstr += lines[0]; | |||||
if (lines.length === 1) | |||||
return; | |||||
ondata(JSON.parse(currstr)); | |||||
for (var i = 1; i < lines.length -1; ++i) { | |||||
ondata(JSON.parse(lines[i])); | |||||
} | |||||
currstr = lines[lines.length - 1]; | |||||
}); | |||||
child.stderr.on("data", d => console.error(d.toString())); | |||||
var listq = []; | |||||
var monitors = {}; | |||||
function ondata(obj) { | |||||
if (obj instanceof Array) { | |||||
if (listq.length === 0) | |||||
return; | |||||
var cb = listq.shift(); | |||||
cb(obj); | |||||
} else { | |||||
var ss = obj.SUBSYSTEM; | |||||
if (monitors[ss]) { | |||||
monitors[ss].forEach(cb => cb(obj)); | |||||
} | |||||
if (monitors["*"]) { | |||||
monitors["*"].forEach(cb => cb(obj)); | |||||
} | |||||
} | |||||
} | |||||
function list(ss, cb) { | |||||
listq.push(cb); | |||||
child.stdin.write("list:"+ss+"\n"); | |||||
} | |||||
function monitor(ss, cb) { | |||||
if (monitors[ss] == null) { | |||||
child.stdin.write("monitor:"+ss+"\n"); | |||||
monitors[ss] = []; | |||||
} | |||||
monitors[ss].push(cb); | |||||
} | |||||
function unmonitor(ss, cb) { | |||||
if (monitors[ss] == null) | |||||
throw new Error("Callback function for "+ss+" not registered"); | |||||
var removed = false; | |||||
for (var i in monitors[ss]) { | |||||
if (monitors[ss][i] === cb) { | |||||
monitors[ss].splice(i, 1); | |||||
removed = true; | |||||
} | |||||
} | |||||
} | |||||
function exit() { | |||||
child.kill("SIGTERM"); | |||||
} |
#include "json.h" | |||||
#include <stdio.h> | |||||
void json_str(FILE *f, const char *str) | |||||
{ | |||||
int start, i; | |||||
char c; | |||||
fprintf(f, "\""); | |||||
start = 0; | |||||
i = 0; | |||||
while (1) | |||||
{ | |||||
c = str[i]; | |||||
if (c == '\0') | |||||
{ | |||||
fwrite(str + start, 1, i - start, f); | |||||
break; | |||||
} | |||||
else if (c == '\\' || c == '\"') | |||||
{ | |||||
fwrite(str + start, 1, i - start, f); | |||||
fprintf(f, "\\%c", str[i]); | |||||
start = i + 1; | |||||
} | |||||
i += 1; | |||||
} | |||||
fprintf(f, "\""); | |||||
} | |||||
void json_sep(FILE *f) | |||||
{ | |||||
fprintf(f, ","); | |||||
} | |||||
void json_obj_start(FILE *f) | |||||
{ | |||||
fprintf(f, "{"); | |||||
} | |||||
void json_obj_key(FILE *f, const char *key) | |||||
{ | |||||
json_str(f, key); | |||||
fprintf(f, ":"); | |||||
} | |||||
void json_obj_end(FILE *f) | |||||
{ | |||||
fprintf(f, "}"); | |||||
} | |||||
void json_arr_start(FILE *f) | |||||
{ | |||||
fprintf(f, "["); | |||||
} | |||||
void json_arr_end(FILE *f) | |||||
{ | |||||
fprintf(f, "]"); | |||||
} |
#ifndef JSON_H | |||||
#define JSON_H | |||||
#include <stdio.h> | |||||
void json_str(FILE *f, const char *str); | |||||
void json_sep(FILE *f); | |||||
void json_obj_start(FILE *f); | |||||
void json_obj_key(FILE *f, const char *key); | |||||
void json_obj_end(FILE *f); | |||||
void json_arr_start(FILE *f); | |||||
void json_arr_end(FILE *f); | |||||
#endif |
#include "json.h" | |||||
#include <libudev.h> | |||||
#include <stdio.h> | |||||
#include <sys/select.h> | |||||
#include <unistd.h> | |||||
#include <errno.h> | |||||
#include <string.h> | |||||
#include <fcntl.h> | |||||
#include <stdlib.h> | |||||
struct udev *udev; | |||||
FILE *out; | |||||
struct listener | |||||
{ | |||||
struct udev_monitor *mon; | |||||
int fd; | |||||
}; | |||||
struct listener listeners[32]; | |||||
int listenerc = 0; | |||||
struct udev_monitor *mons[32]; | |||||
static void devinfo_print_json( | |||||
FILE *f, | |||||
struct udev *udev, | |||||
struct udev_device *dev) | |||||
{ | |||||
struct udev_list_entry *entry, *sysattrs; | |||||
int first; | |||||
const char *key, *val; | |||||
json_obj_start(f); | |||||
sysattrs = udev_device_get_properties_list_entry(dev); | |||||
first = 1; | |||||
udev_list_entry_foreach(entry, sysattrs) | |||||
{ | |||||
key = udev_list_entry_get_name(entry); | |||||
val = udev_list_entry_get_value(entry); | |||||
if (!first) | |||||
json_sep(f); | |||||
json_obj_key(f, key); | |||||
json_str(f, val); | |||||
first = 0; | |||||
} | |||||
json_obj_end(f); | |||||
udev_device_unref(dev); | |||||
} | |||||
static void enumerate(FILE *f, const char *subsystem) | |||||
{ | |||||
struct udev_enumerate *en; | |||||
struct udev_list_entry *device, *devices; | |||||
struct udev_device *dev; | |||||
int first; | |||||
const char *name; | |||||
// Create enumerator | |||||
en = udev_enumerate_new(udev); | |||||
if (subsystem != NULL) | |||||
udev_enumerate_add_match_subsystem(en, subsystem); | |||||
json_arr_start(f); | |||||
// Enumerate | |||||
udev_enumerate_scan_devices(en); | |||||
devices = udev_enumerate_get_list_entry(en); | |||||
first = 1; | |||||
udev_list_entry_foreach(device, devices) | |||||
{ | |||||
if (!first) | |||||
json_sep(f); | |||||
name = udev_list_entry_get_name(device); | |||||
dev = udev_device_new_from_syspath(udev, name); | |||||
devinfo_print_json(f, udev, dev); | |||||
first = 0; | |||||
} | |||||
json_arr_end(f); | |||||
printf("\n"); | |||||
fflush(f); | |||||
udev_enumerate_unref(en); | |||||
} | |||||
int monitor(const char *subsystem) | |||||
{ | |||||
struct udev_monitor *mon; | |||||
struct listener *l; | |||||
mon = udev_monitor_new_from_netlink(udev, "udev"); | |||||
if (!mon) | |||||
return -1; | |||||
if (subsystem != NULL) | |||||
{ | |||||
udev_monitor_filter_add_match_subsystem_devtype( | |||||
mon, subsystem, NULL); | |||||
} | |||||
udev_monitor_enable_receiving(mon); | |||||
l = &listeners[listenerc++]; | |||||
l->mon = mon; | |||||
l->fd = udev_monitor_get_fd(mon); | |||||
return 0; | |||||
} | |||||
void oncommand(struct listener *l) | |||||
{ | |||||
char buf[1024]; | |||||
char *cmd, *arg; | |||||
int cnt, i; | |||||
char c; | |||||
memset(buf, 0, sizeof(buf)); | |||||
cnt = read(l->fd, buf, sizeof(buf)); | |||||
if (cnt == -1) | |||||
{ | |||||
perror("stdin"); | |||||
return; | |||||
} | |||||
else if (cnt == 0) | |||||
{ | |||||
// don't try to read from stdin anymore if it's closed | |||||
l->fd = -1; | |||||
return; | |||||
} | |||||
else if (cnt == sizeof(buf)) | |||||
{ | |||||
fprintf(stderr, "input buffer too big\n"); | |||||
// drain, so we don't end up reading stale data next time | |||||
do { | |||||
cnt = read(l->fd, buf, sizeof(buf)); | |||||
if (cnt == -1) | |||||
{ | |||||
perror("stdin"); | |||||
return; | |||||
} | |||||
} while (cnt == sizeof(buf)); | |||||
return; | |||||
} | |||||
// parse input into command and argument | |||||
cmd = buf; | |||||
arg = NULL; | |||||
i = 0; | |||||
while (1) | |||||
{ | |||||
c = buf[i]; | |||||
// colon: replace with \0 to terminate cmd, | |||||
// update arg to point to the charcater after : | |||||
if (c == ':') | |||||
{ | |||||
buf[i] = '\0'; | |||||
arg = buf + i + 1; | |||||
} | |||||
// newline: replace with \0 to terminate arg, | |||||
// respond to request, then update cmd and | |||||
// continue reading the buffer until we reach a \0 | |||||
else if (c == '\n' || c == '\0') | |||||
{ | |||||
buf[i] = '\0'; | |||||
if (*cmd == '\0') | |||||
break; | |||||
// respond to request | |||||
if (strcmp(cmd, "list") == 0) | |||||
{ | |||||
if (strcmp(arg, "*") == 0) | |||||
enumerate(out, NULL); | |||||
else | |||||
enumerate(out, arg); | |||||
} | |||||
else if (strcmp(cmd, "monitor") == 0) | |||||
{ | |||||
if (strcmp(arg, "*") == 0) | |||||
monitor(NULL); | |||||
else | |||||
monitor(arg); | |||||
} | |||||
else | |||||
{ | |||||
fprintf(stderr, "Unknown command: %s\n", cmd); | |||||
} | |||||
if (c == '\0') | |||||
break; | |||||
cmd = buf + i + 1; | |||||
} | |||||
i += 1; | |||||
} | |||||
} | |||||
void onevent(struct listener *l) | |||||
{ | |||||
struct udev_device *dev = udev_monitor_receive_device(l->mon); | |||||
if (!dev) | |||||
{ | |||||
fprintf(stderr, "No device even though an event occurred.\n"); | |||||
return; | |||||
} | |||||
devinfo_print_json(out, udev, dev); | |||||
printf("\n"); | |||||
fflush(out); | |||||
} | |||||
void readinput() | |||||
{ | |||||
fd_set set; | |||||
FD_ZERO(&set); | |||||
int max = -1; | |||||
for (int i = 0; i < listenerc; ++i) | |||||
{ | |||||
int fd = listeners[i].fd; | |||||
if (fd == -1) | |||||
continue; | |||||
FD_SET(fd, &set); | |||||
if (fd > max) | |||||
max = fd; | |||||
} | |||||
int activity; | |||||
activity = select(max + 1, &set, NULL, NULL, NULL); | |||||
if (activity < 0) | |||||
{ | |||||
if (errno != EINTR) | |||||
perror("select"); | |||||
return; | |||||
} | |||||
for (int i = 0; i < listenerc; ++i) | |||||
{ | |||||
struct listener *l = &listeners[i]; | |||||
int fd = l->fd; | |||||
if (!FD_ISSET(fd, &set)) | |||||
continue; | |||||
if (fd == STDIN_FILENO) | |||||
oncommand(l); | |||||
else | |||||
onevent(l); | |||||
} | |||||
} | |||||
int main() | |||||
{ | |||||
out = stdout; | |||||
memset(listeners, 0, sizeof(listeners)); | |||||
// read stdin | |||||
listeners[listenerc++].fd = STDIN_FILENO; | |||||
// create udev | |||||
udev = udev_new(); | |||||
if (!udev) | |||||
{ | |||||
fprintf(stderr, "Failed to craete udev\n"); | |||||
return 1; | |||||
} | |||||
// main loop | |||||
while (1) { | |||||
readinput(); | |||||
} | |||||
udev_unref(udev); | |||||
} |