Example #1
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);
    (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;
}
Example #5
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;
}
Example #11
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");
    }
}
Example #12
0
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;
}
Example #13
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);
Example #14
0
            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;
    }