Esempio n. 1
0
int main(int argc, char *argv[]) {
    struct passwd *pw;
    char *username;
    char *image_path = NULL;
    int ret;
    struct pam_conv conv = {conv_callback, NULL};
    int curs_choice = CURS_NONE;
    int o;
    int optind = 0;
    struct option longopts[] = {
        {"version", no_argument, NULL, 'v'},
        {"nofork", no_argument, NULL, 'n'},
        {"beep", no_argument, NULL, 'b'},
        {"dpms", no_argument, NULL, 'd'},
        {"color", required_argument, NULL, 'c'},
        {"pointer", required_argument, NULL, 'p'},
        {"debug", no_argument, NULL, 0},
        {"help", no_argument, NULL, 'h'},
        {"no-unlock-indicator", no_argument, NULL, 'u'},
        {"image", required_argument, NULL, 'i'},
        {"tiling", no_argument, NULL, 't'},
        {"fuzzy", no_argument, NULL, 'f'},
        {"radius", required_argument, NULL, 'r'},
        {"sigma", required_argument, NULL, 's'},
        {"ignore-empty-password", no_argument, NULL, 'e'},
        {"inactivity-timeout", required_argument, NULL, 'I'},
        {"show-failed-attempts", no_argument, NULL, 'f'},
        {NULL, no_argument, NULL, 0}};

    if ((pw = getpwuid(getuid())) == NULL)
        err(EXIT_FAILURE, "getpwuid() failed");
    if ((username = pw->pw_name) == NULL)
        errx(EXIT_FAILURE, "pw->pw_name is NULL.\n");

    char *optstring = "hvnbdc:p:ui:tfr:s:eI:l";
    while ((o = getopt_long(argc, argv, optstring, longopts, &optind)) != -1) {
        switch (o) {
            case 'v':
                errx(EXIT_SUCCESS,
                     "version " VERSION " © 2010 Michael Stapelberg");
            case 'n':
                dont_fork = true;
                break;
            case 'b':
                beep = true;
                break;
            case 'd':
                fprintf(stderr, "DPMS support has been removed from i3lock. "
                                "Please see the manpage i3lock(1).\n");
                break;
            case 'I': {
                int time = 0;
                if (sscanf(optarg, "%d", &time) != 1 || time < 0)
                    errx(EXIT_FAILURE,
                         "invalid timeout, it must be a positive integer\n");
                inactivity_timeout = time;
                break;
            }
            case 'c': {
                char *arg = optarg;

                /* Skip # if present */
                if (arg[0] == '#')
                    arg++;

                if (strlen(arg) != 6 ||
                    sscanf(arg, "%06[0-9a-fA-F]", color) != 1)
                    errx(EXIT_FAILURE, "color is invalid, it must be given in "
                                       "3-byte hexadecimal format: rrggbb\n");
                break;
            }
            case 'u':
                unlock_indicator = false;
                break;
            case 'i':
                image_path = strdup(optarg);
                break;
            case 't':
                tile = true;
                break;
            case 'f':
                fuzzy = true;
                break;
            case 'r':
                sscanf(optarg, "%d", &blur_radius);
                break;
            case 's':
                sscanf(optarg, "%f", &blur_sigma);
                break;
            case 'p':
                if (!strcmp(optarg, "win")) {
                    curs_choice = CURS_WIN;
                } else if (!strcmp(optarg, "default")) {
                    curs_choice = CURS_DEFAULT;
                } else {
                    errx(EXIT_FAILURE, "i3lock: Invalid pointer type given. "
                                       "Expected one of \"win\" or "
                                       "\"default\".\n");
                }
                break;
            case 'e':
                ignore_empty_password = true;
                break;
            case 0:
                if (strcmp(longopts[optind].name, "debug") == 0)
                    debug_mode = true;
                break;
            case 'l':
                show_failed_attempts = true;
                break;
            default:
                errx(EXIT_FAILURE, "Syntax: i3lock [-v] [-n] [-b] [-d] [-c "
                                   "color] [-u] [-p win|default]"
                                   " [-i image.png] [-t] [-f] [-r radius] [-s "
                                   "sigma] [-e] [-I timeout] [-l]");
        }
    }

    /* We need (relatively) random numbers for highlighting a random part of
     * the unlock indicator upon keypresses. */
    srand(time(NULL));

    /* Initialize PAM */
    ret = pam_start("i3lock", username, &conv, &pam_handle);
    if (ret != PAM_SUCCESS)
        errx(EXIT_FAILURE, "PAM: %s", pam_strerror(pam_handle, ret));

/* Using mlock() as non-super-user seems only possible in Linux. Users of other
 * operating systems should use encrypted swap/no swap (or remove the ifdef and
 * run i3lock as super-user). */
#if defined(__linux__)
    /* Lock the area where we store the password in memory, we don’t want it to
     * be swapped to disk. Since Linux 2.6.9, this does not require any
     * privileges, just enough bytes in the RLIMIT_MEMLOCK limit. */
    if (mlock(password, sizeof(password)) != 0)
        err(EXIT_FAILURE,
            "Could not lock page in memory, check RLIMIT_MEMLOCK");
#endif

    /* Initialize connection to X11 */
    if ((display = XOpenDisplay(NULL)) == NULL)
        errx(EXIT_FAILURE,
             "Could not connect to X11, maybe you need to set DISPLAY?");
    XSetEventQueueOwner(display, XCBOwnsEventQueue);
    conn = XGetXCBConnection(display);
    /* Double checking that connection is good and operatable with xcb */
    if (xcb_connection_has_error(conn))
        errx(EXIT_FAILURE,
             "Could not connect to X11, maybe you need to set DISPLAY?");

    if (xkb_x11_setup_xkb_extension(
            conn, XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION,
            0, NULL, NULL, &xkb_base_event, &xkb_base_error) != 1)
        errx(EXIT_FAILURE, "Could not setup XKB extension.");

    static const xcb_xkb_map_part_t required_map_parts =
        (XCB_XKB_MAP_PART_KEY_TYPES | XCB_XKB_MAP_PART_KEY_SYMS |
         XCB_XKB_MAP_PART_MODIFIER_MAP | XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS |
         XCB_XKB_MAP_PART_KEY_ACTIONS | XCB_XKB_MAP_PART_VIRTUAL_MODS |
         XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP);

    static const xcb_xkb_event_type_t required_events =
        (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY |
         XCB_XKB_EVENT_TYPE_MAP_NOTIFY | XCB_XKB_EVENT_TYPE_STATE_NOTIFY);

    xcb_xkb_select_events(conn, xkb_x11_get_core_keyboard_device_id(conn),
                          required_events, 0, required_events,
                          required_map_parts, required_map_parts, 0);

    /* When we cannot initially load the keymap, we better exit */
    if (!load_keymap())
        errx(EXIT_FAILURE, "Could not load keymap");

    const char *locale = getenv("LC_ALL");
    if (!locale)
        locale = getenv("LC_CTYPE");
    if (!locale)
        locale = getenv("LANG");
    if (!locale) {
        if (debug_mode)
            fprintf(stderr, "Can't detect your locale, fallback to C\n");
        locale = "C";
    }

    load_compose_table(locale);

    xinerama_init();
    xinerama_query_screens();

    /* check if the X server supports DPMS */
    xcb_dpms_capable_cookie_t dpmsc = xcb_dpms_capable(conn);
    xcb_dpms_capable_reply_t *dpmsr;
    if ((dpmsr = xcb_dpms_capable_reply(conn, dpmsc, NULL))) {
        dpms_capable = dpmsr->capable;
        if (!dpmsr->capable && dpms) {
            if (debug_mode)
                fprintf(stderr, "Disabling DPMS, X server not DPMS capable\n");
            dpms = false;
        }
        free(dpmsr);
    }

    screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;

    last_resolution[0] = screen->width_in_pixels;
    last_resolution[1] = screen->height_in_pixels;

    xcb_change_window_attributes(conn, screen->root, XCB_CW_EVENT_MASK,
                                 (uint32_t[]){XCB_EVENT_MASK_STRUCTURE_NOTIFY});

    if (image_path && !fuzzy) {
        /* Create a pixmap to render on, fill it with the background color */
        img = cairo_image_surface_create_from_png(image_path);
        /* In case loading failed, we just pretend no -i was specified. */
        if (cairo_surface_status(img) != CAIRO_STATUS_SUCCESS) {
            fprintf(stderr, "Could not load image \"%s\": %s\n", image_path,
                    cairo_status_to_string(cairo_surface_status(img)));
            img = NULL;
        }
    }

    if (fuzzy) {
        init_blur_coefficents();
    }

    /* Pixmap on which the image is rendered to (if any) */
    xcb_pixmap_t bg_pixmap = draw_image(last_resolution);

    /* open the fullscreen window, already with the correct pixmap in place */
    if (fuzzy) {
        win = open_overlay_window(conn, screen);
    } else {
        win = open_fullscreen_window(conn, screen, color, bg_pixmap);
    }
    xcb_free_pixmap(conn, bg_pixmap);

    if (fuzzy) {
        /* Set up damage notifications */
        set_up_damage_notifications(conn, screen);
    } else {
        pid_t pid = fork();
        /* The pid == -1 case is intentionally ignored here:
         * While the child process is useful for preventing other windows from
         * popping up while i3lock blocks, it is not critical. */
        if (pid == 0) {
            /* Child */
            close(xcb_get_file_descriptor(conn));
            raise_loop(win);
            exit(EXIT_SUCCESS);
        }
    }
    cursor = create_cursor(conn, screen, win, curs_choice);

    grab_pointer_and_keyboard(conn, screen, cursor);
    /* Load the keymap again to sync the current modifier state. Since we first
     * loaded the keymap, there might have been changes, but starting from now,
     * we should get all key presses/releases due to having grabbed the
     * keyboard. */
    (void)load_keymap();

    /* Initialize the libev event loop. */
    main_loop = EV_DEFAULT;
    if (main_loop == NULL)
        errx(EXIT_FAILURE, "Could not initialize libev. Bad LIBEV_FLAGS?\n");

    struct ev_io *xcb_watcher = calloc(sizeof(struct ev_io), 1);
    struct ev_check *xcb_check = calloc(sizeof(struct ev_check), 1);
    struct ev_prepare *xcb_prepare = calloc(sizeof(struct ev_prepare), 1);

    ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn),
               EV_READ);
    ev_io_start(main_loop, xcb_watcher);

    ev_check_init(xcb_check, xcb_check_cb);
    ev_check_start(main_loop, xcb_check);

    ev_prepare_init(xcb_prepare, xcb_prepare_cb);
    ev_prepare_start(main_loop, xcb_prepare);

    /* Invoke the event callback once to catch all the events which were
     * received up until now. ev will only pick up new events (when the X11
     * file descriptor becomes readable). */
    ev_invoke(main_loop, xcb_check, 0);
    /* usually fork is called from mapnotify event handler, but in our case
     * a new window is not created and so the mapnotify event doesn't come */
    if (fuzzy && !dont_fork) {
        dont_fork = true;

        /* In the parent process, we exit */
        if (fork() != 0)
            exit(0);

        ev_loop_fork(EV_DEFAULT);
    }
    ev_loop(main_loop, 0);
}
Esempio n. 2
0
int main(int argc, char *argv[]) {
    setlocale(LC_TIME, "ru_RU.UTF-8");
    char *username;
    char *image_path = NULL;
    int ret;
    struct pam_conv conv = {conv_callback, NULL};
    int curs_choice = CURS_NONE;
    int o;
    int optind = 0;
    struct option longopts[] = {
        {"version", no_argument, NULL, 'v'},
        {"nofork", no_argument, NULL, 'n'},
        {"beep", no_argument, NULL, 'b'},
        {"dpms", no_argument, NULL, 'd'},
        {"color", required_argument, NULL, 'c'},
        {"pointer", required_argument, NULL , 'p'},
        {"debug", no_argument, NULL, 0},
        {"help", no_argument, NULL, 'h'},
        {"no-unlock-indicator", no_argument, NULL, 'u'},
        {"image", required_argument, NULL, 'i'},
        {"tiling", no_argument, NULL, 't'},
	{"auth-fail-command", required_argument, NULL, 'f'},
	{"auth-done-command", required_argument, NULL, 'o'},
        {NULL, no_argument, NULL, 0}
    };

    if ((username = getenv("USER")) == NULL)
        errx(1, "USER environment variable not set, please set it.\n");

    while ((o = getopt_long(argc, argv, "hvnbdc:p:ui:f:o:t", longopts, &optind)) != -1) {
        switch (o) {
        case 'v':
            errx(EXIT_SUCCESS, "version " VERSION " © 2010-2012 Michael Stapelberg");
        case 'n':
            dont_fork = true;
            break;
        case 'b':
            beep = true;
            break;
        case 'd':
            dpms = true;
            break;
        case 'c': {
            char *arg = optarg;

            /* Skip # if present */
            if (arg[0] == '#')
                arg++;

            if (strlen(arg) != 6 || sscanf(arg, "%06[0-9a-fA-F]", color) != 1)
                errx(1, "color is invalid, it must be given in 3-byte hexadecimal format: rrggbb\n");

            break;
        }
        case 'u':
            unlock_indicator = false;
            break;
        case 'i':
            image_path = strdup(optarg);
            break;
        case 't':
            tile = true;
            break;
        case 'p':
            if (!strcmp(optarg, "win")) {
                curs_choice = CURS_WIN;
            } else if (!strcmp(optarg, "default")) {
                curs_choice = CURS_DEFAULT;
            } else {
                errx(1, "i3lock: Invalid pointer type given. Expected one of \"win\" or \"default\".\n");
            }
            break;
	case 'f':
	    shell_auth_fail_command = strdup(optarg);
	    break;
	case 'o':
	    shell_auth_done_command = strdup(optarg);
	    break;
        case 'h':
            show_time = false;
            break;
        case 0:
            if (strcmp(longopts[optind].name, "debug") == 0)
                debug_mode = true;
            break;
        default:
            errx(1, "Syntax: i3lock [-v] [-n] [-b] [-d] [-c color] [-u] [-p win|default] [-h]"
            " [-i image.png] [-t] [-f command] [-o command]"
            );
        }
    }

    /* We need (relatively) random numbers for highlighting a random part of
     * the unlock indicator upon keypresses. */
    srand(time(NULL));

    /* Initialize PAM */
    ret = pam_start("i3lock", username, &conv, &pam_handle);
    if (ret != PAM_SUCCESS)
        errx(EXIT_FAILURE, "PAM: %s", pam_strerror(pam_handle, ret));

/* Using mlock() as non-super-user seems only possible in Linux. Users of other
 * operating systems should use encrypted swap/no swap (or remove the ifdef and
 * run i3lock as super-user). */
#if defined(__linux__)
    /* Lock the area where we store the password in memory, we don’t want it to
     * be swapped to disk. Since Linux 2.6.9, this does not require any
     * privileges, just enough bytes in the RLIMIT_MEMLOCK limit. */
    if (mlock(password, sizeof(password)) != 0)
        err(EXIT_FAILURE, "Could not lock page in memory, check RLIMIT_MEMLOCK");
#endif

    /* Initialize connection to X11 */
    if ((display = XOpenDisplay(NULL)) == NULL)
        errx(EXIT_FAILURE, "Could not connect to X11, maybe you need to set DISPLAY?");
    XSetEventQueueOwner(display, XCBOwnsEventQueue);
    conn = XGetXCBConnection(display);

    /* Double checking that connection is good and operatable with xcb */
    if (xcb_connection_has_error(conn))
        errx(EXIT_FAILURE, "Could not connect to X11, maybe you need to set DISPLAY?");

    /* When we cannot initially load the keymap, we better exit */
    if (!load_keymap())
        errx(EXIT_FAILURE, "Could not load keymap");

    xinerama_init();
    xinerama_query_screens();

    /* if DPMS is enabled, check if the X server really supports it */
    if (dpms) {
        xcb_dpms_capable_cookie_t dpmsc = xcb_dpms_capable(conn);
        xcb_dpms_capable_reply_t *dpmsr;
        if ((dpmsr = xcb_dpms_capable_reply(conn, dpmsc, NULL))) {
            if (!dpmsr->capable) {
                if (debug_mode)
                    fprintf(stderr, "Disabling DPMS, X server not DPMS capable\n");
                dpms = false;
            }
            free(dpmsr);
        }
    }

    screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;

    last_resolution[0] = screen->width_in_pixels;
    last_resolution[1] = screen->height_in_pixels;

    xcb_change_window_attributes(conn, screen->root, XCB_CW_EVENT_MASK,
            (uint32_t[]){ XCB_EVENT_MASK_STRUCTURE_NOTIFY });

    if (image_path) {
        /* Create a pixmap to render on, fill it with the background color */
        img = cairo_image_surface_create_from_png(image_path);
        /* In case loading failed, we just pretend no -i was specified. */
        if (cairo_surface_status(img) != CAIRO_STATUS_SUCCESS) {
            fprintf(stderr, "Could not load image \"%s\": cairo surface status %d\n",
                    image_path, cairo_surface_status(img));
            img = NULL;
        }
    }

    /* Pixmap on which the image is rendered to (if any) */
    xcb_pixmap_t bg_pixmap = draw_image(last_resolution);

    /* open the fullscreen window, already with the correct pixmap in place */
    win = open_fullscreen_window(conn, screen, color, bg_pixmap);
    xcb_free_pixmap(conn, bg_pixmap);

    cursor = create_cursor(conn, screen, win, curs_choice);

    grab_pointer_and_keyboard(conn, screen, cursor);

    if (dpms)
        dpms_turn_off_screen(conn);

    /* Initialize the libev event loop. */
    main_loop = EV_DEFAULT;
    if (main_loop == NULL)
        errx(EXIT_FAILURE, "Could not initialize libev. Bad LIBEV_FLAGS?\n");

    struct ev_io *xcb_watcher = calloc(sizeof(struct ev_io), 1);
    struct ev_check *xcb_check = calloc(sizeof(struct ev_check), 1);
    struct ev_prepare *xcb_prepare = calloc(sizeof(struct ev_prepare), 1);

    ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ);
    ev_io_start(main_loop, xcb_watcher);

    ev_check_init(xcb_check, xcb_check_cb);
    ev_check_start(main_loop, xcb_check);

    ev_prepare_init(xcb_prepare, xcb_prepare_cb);
    ev_prepare_start(main_loop, xcb_prepare);

    /* Invoke the event callback once to catch all the events which were
     * received up until now. ev will only pick up new events (when the X11
     * file descriptor becomes readable). */

    ev_invoke(main_loop, xcb_check, 0);

    if(show_time)
        start_time_redraw_tick();

    ev_loop(main_loop, 0);
}