static void lgtd_signal_event_callback(int signum, short events, void *ctx) { int i = (int)ctx; assert(i >= 0); assert(i < (int)LGTD_ARRAY_SIZE(lgtd_signals)); assert(i < (int)LGTD_ARRAY_SIZE(lgtd_signal_evs)); assert(signum == lgtd_signals[i]); lgtd_last_signal_received = signum; event_del(lgtd_signal_evs[i]); // restore default behavior event_base_loopbreak(lgtd_ev_base); (void)events; }
int main(void) { jsmntok_t tokens[32]; memset(tokens, 0, sizeof(tokens)); const char json[] = "[[\"*\"],[1,2,3,4]]"; int parsed = parse_json( tokens, LGTD_ARRAY_SIZE(tokens), json, sizeof(json) ); struct lgtd_jsonrpc_target_args { const jsmntok_t *target; int target_ntokens; const jsmntok_t *label; } params = { NULL, 0, NULL }; static const struct lgtd_jsonrpc_node schema[] = { LGTD_JSONRPC_NODE( "target", offsetof(struct lgtd_jsonrpc_target_args, target), offsetof(struct lgtd_jsonrpc_target_args, target_ntokens), lgtd_jsonrpc_type_string_number_or_array, false ), LGTD_JSONRPC_NODE( "label", offsetof(struct lgtd_jsonrpc_target_args, label), -1, // this must dereference json from the what's in the token (see // next comment): lgtd_jsonrpc_type_number, false ) };
static void test_request(const char *json) { jsmntok_t tokens[32]; int parsed = parse_json( tokens, LGTD_ARRAY_SIZE(tokens), json, strlen(json) ); bool ok; struct lgtd_jsonrpc_request req = TEST_REQUEST_INITIALIZER; struct lgtd_client client = { .io = NULL, .current_request = &req, .json = json }; ok = lgtd_jsonrpc_check_and_extract_request(&req, tokens, parsed, json); if (!ok) { errx(1, "can't parse request"); } lgtd_jsonrpc_check_and_call_set_light_from_hsbk(&client); if (set_light_called) { errx(1, "lgtd_proto_power_off was called"); } reset_client_write_buf(); }
int main(void) { jsmntok_t tokens[32]; const char json[] = ("{" "\"jsonrpc\": \"2.0\"," "\"method\": \"power_off\"," "\"params\": {\"target\": \"*\"}," "\"id\": \"42\"" "}"); int parsed = parse_json( tokens, LGTD_ARRAY_SIZE(tokens), json, sizeof(json) ); bool ok; struct lgtd_jsonrpc_request req = TEST_REQUEST_INITIALIZER; struct lgtd_client client = { .io = NULL, .current_request = &req, .json = json }; ok = lgtd_jsonrpc_check_and_extract_request(&req, tokens, parsed, json); if (!ok) { errx(1, "can't parse request"); } lgtd_jsonrpc_check_and_call_power_off(&client); if (!power_off_called) { errx(1, "lgtd_proto_power_off wasn't called"); } return 0; }
static void lgtd_close_signal_handling(void) { for (int i = 0; i != LGTD_ARRAY_SIZE(lgtd_signals); i++) { event_del(lgtd_signal_evs[i]); event_free(lgtd_signal_evs[i]); } }
static void test_float(const char *json) { jsmntok_t tokens[8]; parse_json(tokens, LGTD_ARRAY_SIZE(tokens), json, strlen(json)); if (!lgtd_jsonrpc_type_float_between_0_and_1(&tokens[1], json)) { errx(1, "%s wasn't considered as a valid float >= 0 and <= 1", json); } }
int main(void) { const char json[] = "[1234]"; jsmntok_t tokens[8]; int rv = parse_json(tokens, LGTD_ARRAY_SIZE(tokens), json, sizeof(json)); printf("rv = %d\n", rv); bool ok = lgtd_jsonrpc_type_integer(&tokens[1], json); if (!ok) { errx(1, "%s wasn't considered as a valid integer", json); } return 0; }
int main(void) { jsmntok_t tokens[32]; const char json[] = ("[" "{" "\"method\": \"power_on\"," "\"id\": \"004daf12-0561-4fbc-bfdb-bfe69cfbf4b5\"," "\"params\": [\"*\"]," "\"jsonrpc\": \"2.0\"" "}," "{" "\"method\": \"la rache\"," "\"id\": \"1f7a32c8-6741-4ee7-bec1-8431c7d514dc\"," "\"params\": [\"*\"]," "\"jsonrpc\": \"2.0\"" "}" "]"); struct lgtd_client client = { .json = json, .jsmn_tokens = tokens }; int parsed = parse_json( tokens, LGTD_ARRAY_SIZE(tokens), json, sizeof(json) ); lgtd_jsonrpc_dispatch_request(&client, parsed); if (!power_on_call_count) { errx(1, "power_on was never called"); } const char expected[] = ("[" "," // we mocked the first function "{" "\"jsonrpc\": \"2.0\", " "\"id\": \"1f7a32c8-6741-4ee7-bec1-8431c7d514dc\", " "\"error\": {\"code\": -32601, \"message\": \"Method not found\"}" "}" "]"); if (strcmp(expected, client_write_buf)) { errx(1, "got client buf %s (expected %s)", client_write_buf, expected); } return 0; }
int main(void) { jsmntok_t tokens[32]; const char json[] = ("{" "\"jsonrpc\": \"2.0\"," "\"method\": \"set_light_from_hsbk\"," "\"params\": {" "\"target\": \"*\", " "\"hue\": 324.2341514, " "\"saturation\": 0.234, " "\"brightness\": 1.0, " "\"kelvin\": 4200," "\"transition\": 60" "}," "\"id\": \"42\"" "}"); int parsed = parse_json( tokens, LGTD_ARRAY_SIZE(tokens), json, sizeof(json) ); bool ok; struct lgtd_jsonrpc_request req = TEST_REQUEST_INITIALIZER; struct lgtd_client client = { .io = NULL, .current_request = &req, .json = json }; ok = lgtd_jsonrpc_check_and_extract_request(&req, tokens, parsed, json); if (!ok) { errx(1, "can't parse request"); } lgtd_jsonrpc_check_and_call_set_light_from_hsbk(&client); if (!set_light_called) { errx(1, "lgtd_proto_set_light_from_hsbk wasn't called"); } return 0; }
int main(void) { jsmntok_t tokens[32]; const char json[] = ("[" "{" "\"method\": \"power_on\"," "\"id\": \"004daf12-0561-4fbc-bfdb-bfe69cfbf4b5\"," "\"params\": [\"*\"]," "\"jsonrpc\": \"2.0\"" "}," "{" "\"method\": \"get_light_state\"," "\"params\": [\"*\"]," "\"jsonrpc\": \"2.0\"" "}" "]"); struct lgtd_client client = { .json = json, .jsmn_tokens = tokens }; int parsed = parse_json( tokens, LGTD_ARRAY_SIZE(tokens), json, sizeof(json) ); lgtd_jsonrpc_dispatch_request(&client, parsed); if (!power_on_call_count) { errx(1, "power_on was never called"); } if (!get_light_state_call_count) { errx(1, "get_light_state was never called"); } const char expected[] = "[]"; // we mocked the functions if (strcmp(expected, client_write_buf)) { errx(1, "got client buf %s (expected %s)", client_write_buf, expected); } return 0; }
static void lgtd_setup_signal_handling(void) { for (int i = 0; i != LGTD_ARRAY_SIZE(lgtd_signals); i++) { lgtd_signal_evs[i] = evsignal_new( lgtd_ev_base, lgtd_signals[i], lgtd_signal_event_callback, // event_self_cbarg() would make things cleaner, but this was // unfortunately added in libevent 2.1 which hasn't been released // as of 2016: (void *)(intptr_t)i // cast twice for -Wint-to-void-pointer-cast ); if (!lgtd_signal_evs[i] || evsignal_add(lgtd_signal_evs[i], NULL)) { lgtd_err(1, "can't configure signal handling"); } } struct sigaction act = { .sa_handler = SIG_IGN }; if (sigaction(SIGPIPE, &act, NULL)) { lgtd_err(1, "can't configure signal handling"); } }
int main(int argc, char *argv[], char *envp[]) { char progname[32] = { 0 }; memcpy(progname, argv[0], LGTD_MIN(sizeof(progname) - 1, strlen(argv[0]))); lgtd_daemon_setup_proctitle(argc, argv, envp); lgtd_configure_libevent(); lgtd_setup_signal_handling(); static const struct option long_opts[] = { {"listen", required_argument, NULL, 'l'}, {"command-pipe", required_argument, NULL, 'c'}, {"socket", required_argument, NULL, 's'}, {"foreground", no_argument, NULL, 'f'}, {"daemonize", no_argument, NULL, 'd'}, {"pidfile", required_argument, NULL, 'p'}, {"user", required_argument, NULL, 'u'}, {"group", required_argument, NULL, 'g'}, {"syslog", no_argument, NULL, 'S'}, {"syslog-facility", required_argument, NULL, 'F'}, {"syslog-ident", required_argument, NULL, 'I'}, {"no-timestamps", no_argument, NULL, 't'}, {"help", no_argument, NULL, 'h'}, {"verbosity", required_argument, NULL, 'v'}, {"version", no_argument, NULL, 'V'}, {"prefix", no_argument, NULL, 'P'}, {"rundir", no_argument, NULL, 'r'}, {NULL, 0, NULL, 0} }; const char short_opts[] = "l:c:s:fdp:u:g:SF:I:thv:V"; if (argc == 1) { lgtd_usage(progname); } for (int rv = getopt_long(argc, argv, short_opts, long_opts, NULL); rv != -1; rv = getopt_long(argc, argv, short_opts, long_opts, NULL)) { switch (rv) { case 'l': (void)0; char *sep = strrchr(optarg, ':'); if (!sep || !sep[1]) { lgtd_usage(progname); } *sep = '\0'; if (!lgtd_listen_open(optarg, sep + 1)) { exit(1); } break; case 'c': if (!lgtd_command_pipe_open(optarg)) { exit(1); } break; case 's': if (!lgtd_listen_unix_open(optarg)) { exit(1); } break; case 'f': lgtd_opts.foreground = true; break; case 'd': lgtd_opts.foreground = false; break; case 'p': lgtd_opts.pidfile = optarg; break; case 'u': lgtd_opts.user = optarg; break; case 'g': lgtd_opts.group = optarg; break; case 'S': lgtd_opts.syslog = true; break; case 'F': lgtd_opts.syslog_facility = lgtd_daemon_syslog_facilitytoi(optarg); break; case 'I': lgtd_opts.syslog_ident = optarg; break; case 't': lgtd_opts.log_timestamps = false; break; case 'h': lgtd_usage(progname); case 'v': for (int i = 0;;) { const char *verbose_levels[] = { "debug", "info", "warning", "error" }; if (!strcasecmp(optarg, verbose_levels[i])) { lgtd_opts.verbosity = i; break; } if (++i == LGTD_ARRAY_SIZE(verbose_levels)) { lgtd_errx(1, "Unknown verbosity level: %s", optarg); } } break; case 'V': printf("%s %s\n", progname, LGTD_VERSION); lgtd_cleanup(); return 0; case 'P': printf( "%s%s\n", LGTD_INSTALL_PREFIX, LGTD_INSTALL_PREFIX[ LGTD_ARRAY_SIZE(LGTD_INSTALL_PREFIX) - 1 ] != '/' ? "/" : "" ); return 0; case 'r': printf( "%s%s\n", LGTD_RUNTIME_DIRECTORY, LGTD_RUNTIME_DIRECTORY[ LGTD_ARRAY_SIZE(LGTD_RUNTIME_DIRECTORY) - 1 ] != '/' ? "/" : "" ); return 0; default: lgtd_usage(progname); } } argc -= optind; argv += optind; // Ideally we should parse the syslog relation options first and call that // before anything can be logged: lgtd_log_setup(); if (lgtd_opts.user) { lgtd_daemon_set_user(lgtd_opts.user); lgtd_daemon_set_group(lgtd_opts.group); // create the pidfile before we drop privileges: if (lgtd_opts.pidfile && !lgtd_daemon_write_pidfile(lgtd_opts.pidfile)) { lgtd_warn("couldn't write pidfile at %s", lgtd_opts.pidfile); } lgtd_daemon_drop_privileges(); } else if (lgtd_opts.group) { lgtd_errx(1, "please, specify an user with the -u option"); } lgtd_daemon_die_if_running_as_root_unless_requested(lgtd_opts.user); lgtd_lifx_wire_setup(); if (!lgtd_lifx_discovery_setup() || !lgtd_lifx_broadcast_setup()) { lgtd_err(1, "can't setup lightsd"); } if (!lgtd_opts.foreground) { lgtd_info("forking into the background now..."); if (!lgtd_daemon_unleash()) { lgtd_err(1, "can't fork to the background"); } } // update the pidfile after we've forked: if (lgtd_opts.pidfile && !lgtd_daemon_write_pidfile(lgtd_opts.pidfile)) { lgtd_warn("couldn't write pidfile at %s", lgtd_opts.pidfile); } lgtd_lifx_discovery_start(); // update at least once: so that if no bulbs are discovered we still get a // clear status line. lgtd_daemon_update_proctitle(); event_base_dispatch(lgtd_ev_base); if (lgtd_last_signal_received) { lgtd_info( "received signal %d (%s), exiting...", lgtd_last_signal_received, strsignal(lgtd_last_signal_received) ); } lgtd_cleanup(); return 0; }
.verbosity = LGTD_INFO, #else .verbosity = LGTD_WARN, #endif .user = NULL, .group = NULL, .syslog = false, .syslog_facility = LOG_DAEMON, .syslog_ident = "lightsd", .pidfile = NULL }; struct event_base *lgtd_ev_base = NULL; static const int lgtd_signals[] = { SIGINT, SIGTERM, SIGQUIT }; static struct event *lgtd_signal_evs[LGTD_ARRAY_SIZE(lgtd_signals)] = { NULL }; static int lgtd_last_signal_received = 0; static void lgtd_signal_event_callback(int signum, short events, void *ctx) { int i = (int)ctx; assert(i >= 0); assert(i < (int)LGTD_ARRAY_SIZE(lgtd_signals)); assert(i < (int)LGTD_ARRAY_SIZE(lgtd_signal_evs)); assert(signum == lgtd_signals[i]); lgtd_last_signal_received = signum; event_del(lgtd_signal_evs[i]); // restore default behavior event_base_loopbreak(lgtd_ev_base);
lgtd_jsonrpc_type_object_or_array, true ), LGTD_JSONRPC_NODE( "id", offsetof(struct lgtd_jsonrpc_request, id), -1, lgtd_jsonrpc_type_string_number_or_null, true ) }; bool ok = lgtd_jsonrpc_extract_values_from_schema_and_dict( request, request_schema, LGTD_ARRAY_SIZE(request_schema), tokens, ntokens, json ); if (!ok) { return false; } request->request_ntokens = 1 + 2 + 2; // dict itself + jsonrpc + method if (request->params) { request->request_ntokens += 1 + request->params_ntokens; } if (request->id) { request->request_ntokens += 2; }