@@ -1 +1,2 @@ | |||
node_modules | |||
udev/udev-monitor |
@@ -3,6 +3,7 @@ | |||
var syscheck = require("./js/syscheck"); | |||
var parseConf = require("./js/parse-conf"); | |||
var async = require("./js/async"); | |||
var udev = require("./udev"); | |||
var modules = { | |||
// display: require("./modules/display"), | |||
@@ -49,6 +50,7 @@ function stopAll(cb) { | |||
} | |||
function onTerm() { | |||
udev.exit(); | |||
stopAll(() => process.exit(1)); | |||
} | |||
@@ -1,4 +1,4 @@ | |||
var udev = require("udev"); | |||
var udev = require("../../udev"); | |||
exports.start = start; | |||
exports.stop = stop; | |||
@@ -8,12 +8,10 @@ var conf; | |||
var logger; | |||
var modules; | |||
var monitor; | |||
function onchange(dev, evt) { | |||
if (evt === "add") | |||
function onchange(dev) { | |||
if (dev.ACTION === "add") | |||
logger.info("display added"); | |||
else if (evt === "change") | |||
else if (dev.ACTION === "change") | |||
logger.info("display changed"); | |||
else | |||
logger.info(dev); | |||
@@ -26,12 +24,11 @@ function start(conf_, logger_, modules_) { | |||
logger = logger_ || logger; | |||
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) { | |||
udev.unmonitor("drm", onchange); | |||
cb(); | |||
} | |||
@@ -1,4 +1,4 @@ | |||
var udev = require("udev"); | |||
var udev = require("../../udev"); | |||
var spawn = require("child_process").spawn; | |||
var exec = require("child_process").exec; | |||
@@ -12,8 +12,6 @@ var conf; | |||
var logger; | |||
var modules; | |||
var monitor; | |||
// Set an xinput property | |||
function setProp(name, val, suppressWarnings) { | |||
var args = [ "--set-prop", name ]; | |||
@@ -90,7 +88,7 @@ function nameMatches(dev, name) { | |||
return true; | |||
} | |||
function onchange(dev, evt) { | |||
function onchange(dev) { | |||
if (!filter(dev)) | |||
return; | |||
@@ -107,9 +105,9 @@ function onchange(dev, evt) { | |||
inputType = "mouse"; | |||
// Log add/change | |||
if (evt === "add") | |||
if (dev.ACTION === "add") | |||
logger.info(inputType, dev.NAME, "added"); | |||
else if (evt === "change") | |||
else if (dev.ACTION === "change") | |||
logger.info(inputType, dev.NAME, "changed"); | |||
// Run through and apply relevant rules | |||
@@ -151,15 +149,12 @@ function start(conf_, logger_, modules_) { | |||
logger = logger_ || logger; | |||
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) { | |||
monitor.close(); | |||
udev.unmonitor("input", onchange); | |||
cb(); | |||
} | |||
@@ -1,10 +1,11 @@ | |||
{ | |||
"name": "dedaemon", | |||
"version": "0.0.1", | |||
"version": "0.1.0", | |||
"description": "", | |||
"main": "daemon.js", | |||
"scripts": { | |||
"test": "echo \"Error: no test specified\" && exit 1" | |||
"test": "echo \"Error: no test specified\" && exit 1", | |||
"postinstall": "make -C udev" | |||
}, | |||
"repository": { | |||
"type": "git", | |||
@@ -13,8 +14,7 @@ | |||
"author": "Martin Dørum Nygaard <martid0311@gmail.com> (http://mort.coffee)", | |||
"license": "ISC", | |||
"dependencies": { | |||
"hconfig": "^0.4.0", | |||
"udev": "^0.4.0" | |||
"hconfig": "^0.4.0" | |||
}, | |||
"bin": { | |||
"dedaemon": "./dedaemon.js" |
@@ -0,0 +1,10 @@ | |||
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) |
@@ -0,0 +1,78 @@ | |||
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"); | |||
} |
@@ -0,0 +1,59 @@ | |||
#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, "]"); | |||
} |
@@ -0,0 +1,16 @@ | |||
#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 |
@@ -0,0 +1,287 @@ | |||
#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); | |||
} |