/* * This file is part of fwm-sysdaemon * config.c * * Copyright (c) 2022 firk (firk@cantconnect.ru) * * 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. * 3. The origin of this software must not be misrepresented; you must * not claim that you wrote the original software. * 4. Altered versions in any form must be plainly marked as such, and * must not be misinterpreted as being the original software. * * This software is provided by the author and contributors `as is' * without any express or implied warranty. */ #include #include #include #include #include #include #include #include #include #include #include #include #define FCL_WITH_VA_LIST #include #include #include "util.h" #include "fwatch.h" #include "get_powerstat.h" #include "config.h" struct params params; static int logfd = -1; #ifdef WTH_SYSLOG int syslog_level = LOG_ERR; #endif extern void printlog(char const *fmt, ...) { va_list arg; char tmp[256]; if(!params.no_stderr) { qlog_set_fd(2); qlog_set_prefix("fwm-sysdaemon"); va_start(arg, fmt); qlog_begin_line(); qlog_vprintf(fmt, arg); qlog_end_line(); va_end(arg); } if(logfd>=0) { qlog_set_fd(logfd); qlog_set_prefix(NULL); va_start(arg, fmt); qlog_begin_line(); qlog_vprintf(fmt, arg); qlog_end_line(); va_end(arg); } #ifdef WITH_SYSLOG if(syslogf) { va_start(arg, fmt); qp_vsnprintf(tmp, sizeof(tmp), fmt, arg); va_end(arg); vsyslog(params.syslogf|syslog_level, "%s", tmp); } #endif } static int parse_option(char const *name, char const *v); extern int parse_cmdline_options(int argc, char **argv) { int i; char const *a; size_t j; char name[30]; #ifdef WTH_SYSLOG params.syslogf = -1; #endif params.poll_interval = -1; params.pwr.minpct = -1; params.pwr.sleepcmd = "/usr/bin/pm-suspend"; params.bl.raw_max = -1; params.bl.max = -1; params.bl.linear = -1; for(i=2; i=sizeof(name)) goto bad; name[j] = 0; if(parse_option(name, (*a)?(a+1):NULL)<0) return -1; } if(params.logfn) while((logfd=open(params.logfn,O_WRONLY|O_APPEND|O_CREAT,0644))<0) if((i=errno)!=EINTR) { printlog("open log file \"%S\" error %{ERR}", params.logfn, i); return -1; } if(params.poll_interval==-1) params.poll_interval = 2; params.pwr.enabled = (!params.pwr.batname || *params.pwr.batname || params.pwr.acname || *params.pwr.acname); params.bl.enabled = (!params.bl.blname || *params.bl.blname); return 0; bad: printlog("bad argument: %s", argv[i]); return -1; } static int parse_conf_file(char const *fn) { char buf[4000], c, *pp; unsigned int sz, p, p0, ln; int fd, r; while((fd = open(fn, O_RDONLY|O_NOCTTY))<0) if(errno!=EINTR) { printlog("open conf-file \"%S\" error %{ERR}", fn, errno); return -1; } sz = 0; r = 1; ln = 1; while(r) { if(sz>=sizeof(buf)) { printlog("conf-file \"%S\" line %u too long", fn, ln); close(fd); return -1; } while((r = read(fd, buf+sz, sizeof(buf)-sz))<0) if(errno!=EINTR) { printlog("read conf-file \"%S\" error %{ERR}", fn, errno); close(fd); return -1; } for(p=sz,p0=0,sz+=r; p=0 && c!=9 && c<32 || c==127)) { printlog("conf-file \"%S\" line %u contains invalid symbols", fn, ln); close(fd); return -1; } } if(p0) if(sz-=p0) bcopy(buf+p0, buf, sz); } close(fd); if(sz) { printlog("conf-file \"%S\" line %u (last) non-terminated", fn, ln); return -1; } return 0; } static int parse_option(char const *name, char const *v) { uint u; if(!strcmp(name,"conf-file")) { if(parse_conf_file(v)<0) printlog("error processing conf-file \"%S\"", v); return 0; } #define STROPT(oname,var) if(!strcmp(name,oname)) { if(!v) goto param; if(params.var) goto dup; params.var=v; return 0; } #define VALUEOPT(oname,dupcond) if(!strcmp(name,oname)) { if(!v) goto param; if(dupcond) goto dup; /* ... } */ STROPT("log", logfn) STROPT("pidfile", pidfn) #ifdef WITH_SYSLOG VALUEOPT("syslog", params.syslogf!=-1) /*{*/ if(!*v) params.syslogf = LOG_DAEMON; else if(!strcasecmp(v,"daemon")) params.syslogf = LOG_DAEMON; else if(!strcasecmp(v,"user")) params.syslogf = LOG_USER; else if(!strcasecmp(v,"local0")) params.syslogf = LOG_LOCAL0; else if(!strcasecmp(v,"local1")) params.syslogf = LOG_LOCAL1; else if(!strcasecmp(v,"local2")) params.syslogf = LOG_LOCAL2; else if(!strcasecmp(v,"local3")) params.syslogf = LOG_LOCAL3; else if(!strcasecmp(v,"local4")) params.syslogf = LOG_LOCAL4; else if(!strcasecmp(v,"local5")) params.syslogf = LOG_LOCAL5; else if(!strcasecmp(v,"local6")) params.syslogf = LOG_LOCAL6; else if(!strcasecmp(v,"local7")) params.syslogf = LOG_LOCAL7; else goto badval; return 0; } #endif VALUEOPT("poll-interval", params.poll_interval!=-1) /*{*/ if(strp_to_uint(&v,&u)<0 || *v && (*v!='s' || v[1])) goto badval; params.poll_interval = (u<1)?1:((u>3600)?3600:u); return 0; } STROPT("status", pwr.statusfn) STROPT("id-battery", pwr.batname) STROPT("id-ac", pwr.acname) STROPT("minpctfile", pwr.minpctfn) VALUEOPT("minpct", params.pwr.minpct!=-1) /*{*/ if(strp_to_uint(&v,&u)<0 || *v && (*v!='%' || v[1]) || u>100) goto badval; params.pwr.minpct = u; return 0; } STROPT("id-backlight", bl.blname) VALUEOPT("bl-raw-max", params.bl.raw_max!=-1) /*{*/ if(strp_to_uint(&v,&u)<0 || *v || (int)u<1) goto badval; params.bl.raw_max = u; return 0; } VALUEOPT("bl-max", params.bl.max!=-1) /*{*/ if(strp_to_uint(&v,&u)<0 || *v || (int)u<1) goto badval; params.bl.max = u; return 0; } if(!strcmp(name,"bl-linear")) { if(v) { printlog("'bl-linear' option requires no parameter"); return -1; } if(params.bl.linear!=-1) goto dup; params.bl.linear = 1; return 0; } STROPT("bl-file", bl.ctlfn) printlog("unknown option: %S", name); return -1; param: printlog("'%s' option needs parameter", name); return -1; dup: printlog("duplicate '%s' option", name); return -1; badval: printlog("bad value for '%s' option", name); return -1; } extern void print_usage(void) { qp_puts(&qp_out, "Usage:\n" " fwm-sysdaemon {daemon|foreground} [options...]\n" "Running mode:\n" " foreground run in foreground + log to stderr\n" " daemon means fork into background + disable stderr logs\n" "Options:\n" " --pidfile=/path/to/pidfile (default: disable pidfile)\n" " --log=/path/to/log (default: disable logfile)\n" " write log to specified file (possibly duplicated to stderr)\n" #ifdef WITH_SYSLOG " --syslog=facility (default: disable syslog)\n" " write log via specified syslog facility (default: USER)\n" #else " --syslog= (not supported in this build)\n" #endif " --poll-interval={sec} (default: 2)\n" " seconds between two status checks\n" " --conf-file=/path/to/conf\n" " path to configuration file, which contains one option per line\n" " if format like \"poll-interval=2\"\n" " multiple and/or nested conf-file options can be specified\n" "Power options:\n" " --status=/path/to/statusfile (default: none)\n" " means update a file with current battery status (for inotify)\n" " --id-battery={battery_name} (default: autodetect)\n" " name of battery in /sys/class/power_supply/\n" " empty to disable battery monitoring\n" " --id-ac={ac_name} (default: autodetect)\n" " name of AC power in /sys/class/power_supply/\n" " empty to disable AC power monitoring\n" " --minpct={minpct} (default: 10)\n" " minimum battery % without triggering suspend\n" " --minpctfile=/path/to/battery.minpct (default: none)\n" " file with readable/writable minpct value\n" " without --minpct= option, will try to read {minpct} value from it\n" "Backlight options:\n" " --id-backlight={backlight_name} (default: autodetect)\n" " name of backlight in /sys/class/backlight/\n" " empty to disable backlight management\n" " --bl-raw-max={max} (default: read from /sys/ max_brightness file)\n" " maximum supported value for backlight brightness\n" " --bl-max={range} (default: 100, imagine percents)\n" " maximum normalized value for backlight brightness\n" " --bl-linear (default: no)\n" " do not do logarithmic conversion for the value\n" " --bl-file=/path/to/backlight.value (default: none)\n" " file with readable/writable backlight brightness value\n" "\n"); qp_flush(&qp_out); }