summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPhil Jones <philj56@gmail.com>2021-10-31 22:16:40 +0000
committerPhil Jones <philj56@gmail.com>2021-10-31 23:12:07 +0000
commit6f199dc746bd377223e9bec60467c9060d1048ff (patch)
treeacce570009d48e3efa6a1232c37070c1e0f456c8 /src
parent7297ac5d9cac676ed6cd4552a6b47204f9db2512 (diff)
Add actual greetd functionality.
Useable, but very barebones — not even any error message reporting.
Diffstat (limited to 'src')
-rw-r--r--src/client.h5
-rw-r--r--src/entry.c9
-rw-r--r--src/entry.h4
-rw-r--r--src/greetd.c110
-rw-r--r--src/greetd.h43
-rw-r--r--src/image.c2
-rw-r--r--src/ipc.c120
-rw-r--r--src/ipc.h13
-rw-r--r--src/log.c2
-rw-r--r--src/main.c163
10 files changed, 453 insertions, 18 deletions
diff --git a/src/client.h b/src/client.h
index d356094..7ebea86 100644
--- a/src/client.h
+++ b/src/client.h
@@ -48,6 +48,11 @@ struct client_state {
struct xkb_state *xkb_state;
struct xkb_context *xkb_context;
struct xkb_keymap *xkb_keymap;
+
+ /* greetd state */
+ const char *username;
+ const char *command;
+ bool submit;
};
#endif /* CLIENT_H */
diff --git a/src/entry.c b/src/entry.c
index 7c34acf..63b6f87 100644
--- a/src/entry.c
+++ b/src/entry.c
@@ -133,15 +133,12 @@ void entry_update(struct entry *entry)
/* Draw our text with Pango. */
color = entry->foreground_color;
cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a);
- //const wchar_t *src = entry->password;
- //wcsrtombs(entry->password_mb, &src, N_ELEM(entry->password_mb), NULL);
size_t len = 0;
for (unsigned int i = 0; i < entry->password_length; i++) {
- len += wcrtomb(entry->password_mb + len, entry->password_character, NULL);
+ len += wcrtomb(entry->password_mb_print + len, entry->password_character, NULL);
}
- entry->password_mb[len] = '\0';
- fprintf(stderr, "%s\n", entry->password_mb);
- pango_layout_set_text(layout, entry->password_mb, -1);
+ entry->password_mb_print[len] = '\0';
+ pango_layout_set_text(layout, entry->password_mb_print, -1);
pango_cairo_update_layout(cr, layout);
pango_cairo_show_layout(cr, layout);
diff --git a/src/entry.h b/src/entry.h
index b1b9670..f487410 100644
--- a/src/entry.h
+++ b/src/entry.h
@@ -6,7 +6,7 @@
#include "image.h"
#include "surface.h"
-#define MAX_PASSWORD_LENGTH 256
+#define MAX_PASSWORD_LENGTH 64
struct entry {
struct surface surface;
@@ -22,7 +22,9 @@ struct entry {
wchar_t password[MAX_PASSWORD_LENGTH];
/* Assume maximum of 4 bytes per wchar_t (for UTF-8) */
char password_mb[4*MAX_PASSWORD_LENGTH];
+ char password_mb_print[4*MAX_PASSWORD_LENGTH];
uint32_t password_length;
+ bool password_visible;
/* Options */
uint32_t font_size;
diff --git a/src/greetd.c b/src/greetd.c
new file mode 100644
index 0000000..eb356af
--- /dev/null
+++ b/src/greetd.c
@@ -0,0 +1,110 @@
+#include "greetd.h"
+#include "ipc.h"
+#include <json-c/json_object.h>
+#include <string.h>
+
+struct json_object *greetd_create_session(const char *username)
+{
+ struct json_object *request = json_object_new_object();
+
+ struct json_object *type = json_object_new_string("create_session");
+ json_object_object_add(request, "type", type);
+
+ struct json_object *name = json_object_new_string(username);
+ json_object_object_add(request, "username", name);
+
+ struct json_object *resp = ipc_submit(request);
+ json_object_put(request);
+ return resp;
+}
+
+struct json_object *greetd_post_auth_message_response(const char *response)
+{
+ struct json_object *request = json_object_new_object();
+
+ struct json_object *type = json_object_new_string("post_auth_message_response");
+ json_object_object_add(request, "type", type);
+
+ if (response != NULL) {
+ struct json_object *resp = json_object_new_string(response);
+ json_object_object_add(request, "response", resp);
+ }
+
+ struct json_object *resp = ipc_submit(request);
+ json_object_put(request);
+ return resp;
+}
+
+struct json_object *greetd_start_session(const char *command)
+{
+ struct json_object *request = json_object_new_object();
+
+ struct json_object *type = json_object_new_string("start_session");
+ json_object_object_add(request, "type", type);
+
+ struct json_object *arr = json_object_new_array_ext(1);
+ struct json_object *cmd = json_object_new_string(command);
+ json_object_array_add(arr, cmd);
+ json_object_object_add(request, "cmd", arr);
+
+ struct json_object *resp = ipc_submit(request);
+ json_object_put(request);
+ return resp;
+}
+
+struct json_object *greetd_cancel_session(void)
+{
+ struct json_object *request = json_object_new_object();
+
+ struct json_object *type = json_object_new_string("cancel_session");
+ json_object_object_add(request, "type", type);
+
+ struct json_object *resp = ipc_submit(request);
+ json_object_put(request);
+ return resp;
+}
+
+enum greetd_response_type greetd_parse_response_type(struct json_object *response)
+{
+ const char *str = json_object_get_string(json_object_object_get(response, "type"));
+ if (!strcmp(str, "success")) {
+ return GREETD_RESPONSE_SUCCESS;
+ }
+ if (!strcmp(str, "error")) {
+ return GREETD_RESPONSE_ERROR;
+ }
+ if (!strcmp(str, "auth_message")) {
+ return GREETD_RESPONSE_AUTH_MESSAGE;
+ }
+ return GREETD_RESPONSE_INVALID;
+}
+
+enum greetd_auth_message_type greetd_parse_auth_message_type(struct json_object *response)
+{
+ const char *str = json_object_get_string(json_object_object_get(response, "auth_message_type"));
+ if (!strcmp(str, "visible")) {
+ return GREETD_AUTH_MESSAGE_VISIBLE;
+ }
+ if (!strcmp(str, "secret")) {
+ return GREETD_AUTH_MESSAGE_SECRET;
+ }
+ if (!strcmp(str, "info")) {
+ return GREETD_AUTH_MESSAGE_INFO;
+ }
+ if (!strcmp(str, "error")) {
+ return GREETD_AUTH_MESSAGE_ERROR;
+ }
+ return GREETD_AUTH_MESSAGE_INVALID;
+}
+
+enum greetd_error_type greetd_parse_error_type(struct json_object *response)
+{
+ const char *str = json_object_get_string(json_object_object_get(response, "error_type"));
+ if (!strcmp(str, "auth_error")) {
+ return GREETD_ERROR_AUTH_ERROR;
+ }
+ if (!strcmp(str, "error")) {
+ return GREETD_ERROR_ERROR;
+ }
+ return GREETD_ERROR_INVALID;
+}
diff --git a/src/greetd.h b/src/greetd.h
new file mode 100644
index 0000000..3901d85
--- /dev/null
+++ b/src/greetd.h
@@ -0,0 +1,43 @@
+#ifndef GREETD_H
+#define GREETD_H
+
+#include <json-c/json_object.h>
+
+enum greetd_request_type {
+ GREETD_REQUEST_CREATE_SESSION,
+ GREETD_REQUEST_POST_AUTH_MESSAGE_RESPONSE,
+ GREETD_REQUEST_START_SESSION,
+ GREETD_REQUEST_CANCEL_SESSION
+};
+
+enum greetd_response_type {
+ GREETD_RESPONSE_INVALID,
+ GREETD_RESPONSE_SUCCESS,
+ GREETD_RESPONSE_ERROR,
+ GREETD_RESPONSE_AUTH_MESSAGE
+};
+
+enum greetd_auth_message_type {
+ GREETD_AUTH_MESSAGE_INVALID,
+ GREETD_AUTH_MESSAGE_VISIBLE,
+ GREETD_AUTH_MESSAGE_SECRET,
+ GREETD_AUTH_MESSAGE_INFO,
+ GREETD_AUTH_MESSAGE_ERROR
+};
+
+enum greetd_error_type {
+ GREETD_ERROR_INVALID,
+ GREETD_ERROR_AUTH_ERROR,
+ GREETD_ERROR_ERROR
+};
+
+[[nodiscard]] struct json_object *greetd_create_session(const char *username);
+[[nodiscard]] struct json_object *greetd_post_auth_message_response(const char *response);
+[[nodiscard]] struct json_object *greetd_start_session(const char *command);
+[[nodiscard]] struct json_object *greetd_cancel_session(void);
+
+enum greetd_response_type greetd_parse_response_type(struct json_object *response);
+enum greetd_auth_message_type greetd_parse_auth_message_type(struct json_object *response);
+enum greetd_error_type greetd_parse_error_type(struct json_object *response);
+
+#endif /* GREETD_H */
diff --git a/src/image.c b/src/image.c
index 5e8c511..07195c1 100644
--- a/src/image.c
+++ b/src/image.c
@@ -11,7 +11,7 @@
void image_load(struct image *image, const char *filename)
{
- log_debug("Loading image %s\n", filename);
+ log_debug("Loading image '%s'\n", filename);
FILE *fp = fopen(filename, "rb");
uint8_t header[HEADER_BYTES];
if (!fp) {
diff --git a/src/ipc.c b/src/ipc.c
new file mode 100644
index 0000000..2e92097
--- /dev/null
+++ b/src/ipc.c
@@ -0,0 +1,120 @@
+#include <json-c/json_object.h>
+#include <json-c/json_tokener.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include "ipc.h"
+#include "log.h"
+
+static int ipc_open(void);
+static int ipc_send(int socket, struct json_object *request);
+static struct json_object *ipc_receive(int socket);
+
+struct json_object *ipc_submit(struct json_object *request)
+{
+ int sock = ipc_open();
+ if (sock == -1) {
+ return NULL;
+ }
+
+ if (ipc_send(sock, request) == -1) {
+ close(sock);
+ return NULL;
+ }
+
+ struct json_object *resp = ipc_receive(sock);
+ close(sock);
+ return resp;
+}
+
+/*
+ * Open a connection to the UNIX socket specified by
+ * the environment variable GREETD_SOCK.
+ *
+ * Returns the socket file descriptor on success, or -1 on failure.
+ */
+int ipc_open(void)
+{
+ char *greetd_sock = getenv("GREETD_SOCK");
+
+ if (greetd_sock == NULL) {
+ log_error("GREETD_SOCK not set.\n");
+ return -1;
+ }
+
+ int sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock == -1) {
+ log_error("Unable to create socket: %s\n", strerror(errno));
+ return -1;
+ }
+
+ struct sockaddr_un remote = { .sun_family = AF_UNIX };
+ strncpy(remote.sun_path, greetd_sock, sizeof(remote.sun_path));
+
+ if (connect(sock, (struct sockaddr *)&remote, sizeof(remote)) == -1) {
+ log_error("Unable to connect to greetd: %s\n", strerror(errno));
+ close(sock);
+ return -1;
+ }
+ return sock;
+}
+
+/*
+ * Send an IPC request to the specified socket.
+ *
+ * Returns 0 on success, or -1 on failure.
+ */
+int ipc_send(int sock, struct json_object *request)
+{
+ const char *str = json_object_to_json_string(request);
+ uint32_t len = strlen(str);
+
+ if (send(sock, &len, sizeof(len), 0) == -1) {
+ log_error("Error sending request size: %s\n", strerror(errno));
+ return -1;
+ }
+
+ if (send(sock, str, len, 0) == -1) {
+ log_error("Error sending request: %s\n", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Receive an IPC response on the specified socket.
+ *
+ * Returns the response on success, or NULL on failure.
+ */
+struct json_object *ipc_receive(int sock)
+{
+ uint32_t len = 0;
+
+ if (recv(sock, &len, sizeof(len), 0) != sizeof(len)) {
+ log_error("Error receiving response size: %s\n", strerror(errno));
+ return NULL;
+ }
+
+ char *buf = malloc(len + 1);
+ if (recv(sock, buf, len, 0) != len) {
+ log_error("Error receiving response: %s\n", strerror(errno));
+ free(buf);
+ return NULL;
+ }
+
+ buf[len] = '\0';
+
+ enum json_tokener_error error;
+ struct json_object *resp = json_tokener_parse_verbose(buf, &error);
+ free(buf);
+
+ if (resp == NULL) {
+ log_error("Error parsing response: %s\n", json_tokener_error_desc(error));
+ }
+ return resp;
+
+}
diff --git a/src/ipc.h b/src/ipc.h
new file mode 100644
index 0000000..30a953a
--- /dev/null
+++ b/src/ipc.h
@@ -0,0 +1,13 @@
+#ifndef IPC_H
+#define IPC_H
+
+#include <json-c/json_object.h>
+
+/*
+ * Submit an IPC request to greetd.
+ *
+ * Returns the response on success, or NULL on failure.
+ */
+struct json_object *ipc_submit(struct json_object *request);
+
+#endif /* IPC_H */
diff --git a/src/log.c b/src/log.c
index a27bc6c..9b8b56e 100644
--- a/src/log.c
+++ b/src/log.c
@@ -29,7 +29,7 @@ void log_debug(const char *const fmt, ...)
va_list args;
va_start(args, fmt);
fprintf(stderr, "[%ld.%03ld][DEBUG]: ", t.tv_sec, t.tv_nsec / 1000000);
- vprintf(fmt, args);
+ vfprintf(stderr, fmt, args);
va_end(args);
}
diff --git a/src/main.c b/src/main.c
index 9ed471c..49779f2 100644
--- a/src/main.c
+++ b/src/main.c
@@ -19,6 +19,7 @@
#include "entry.h"
#include "image.h"
#include "gl.h"
+#include "greetd.h"
#include "log.h"
#include "nelem.h"
#include "xdg-shell-client-protocol.h"
@@ -26,6 +27,17 @@
#undef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+static void handle_response(
+ struct client_state *state,
+ struct json_object *response,
+ enum greetd_request_type request);
+static void create_session(struct client_state *state);
+static void start_session(struct client_state *state);
+static void post_auth_message_response(struct client_state *state);
+static void cancel_session(struct client_state *state);
+static void restart_session(struct client_state *state);
+
static void resize(struct client_state *state)
{
struct surface *surface = &state->window.surface;
@@ -192,7 +204,6 @@ static void wl_keyboard_key(
entry->password_length++;
entry->password[entry->password_length] = L'\0';
}
- fprintf(stderr, "%ls\n", entry->password);
} else if (entry->password_length > 0 && sym == XKB_KEY_BackSpace) {
entry->password[entry->password_length - 1] = '\0';
entry->password_length--;
@@ -210,8 +221,15 @@ static void wl_keyboard_key(
} else if (entry->password_length > 0
&& (sym == XKB_KEY_Return
|| sym == XKB_KEY_KP_Enter)) {
+ const wchar_t *src = entry->password;
+ wcsrtombs(
+ entry->password_mb,
+ &src,
+ N_ELEM(entry->password_mb),
+ NULL);
entry->password[0] = '\0';
entry->password_length = 0;
+ client_state->submit = true;
}
entry_update(&client_state->window.entry);
client_state->window.entry.surface.redraw = true;
@@ -359,21 +377,21 @@ static void registry_global(
uint32_t version)
{
struct client_state *state = data;
- if (strcmp(interface, wl_compositor_interface.name) == 0) {
+ if (!strcmp(interface, wl_compositor_interface.name)) {
state->wl_compositor = wl_registry_bind(
wl_registry,
name,
&wl_compositor_interface,
4);
log_debug("Bound to compositor %u.\n", name);
- } else if (strcmp(interface, wl_subcompositor_interface.name) == 0) {
+ } else if (!strcmp(interface, wl_subcompositor_interface.name)) {
state->wl_subcompositor = wl_registry_bind(
wl_registry,
name,
&wl_subcompositor_interface,
1);
log_debug("Bound to subcompositor %u.\n", name);
- } else if (strcmp(interface, wl_seat_interface.name) == 0) {
+ } else if (!strcmp(interface, wl_seat_interface.name)) {
state->wl_seat = wl_registry_bind(
wl_registry,
name,
@@ -384,7 +402,7 @@ static void registry_global(
&wl_seat_listener,
state);
log_debug("Bound to seat %u.\n", name);
- } else if (strcmp(interface, wl_output_interface.name) == 0) {
+ } else if (!strcmp(interface, wl_output_interface.name)) {
state->wl_output = wl_registry_bind(
wl_registry,
name,
@@ -395,7 +413,7 @@ static void registry_global(
&wl_output_listener,
state);
log_debug("Bound to output %u.\n", name);
- } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
+ } else if (!strcmp(interface, xdg_wm_base_interface.name)) {
state->xdg_wm_base = wl_registry_bind(
wl_registry,
name,
@@ -449,6 +467,8 @@ int main(int argc, char *argv[])
{
setlocale(LC_ALL, "");
struct client_state state = {
+ .username = "nobody",
+ .command = "false",
.window = {
.background_color = {0.89, 0.8, 0.824, 1.0},
.scale = 1,
@@ -485,12 +505,14 @@ int main(int argc, char *argv[])
{"text_color", required_argument, NULL, 'T'},
{"font_name", required_argument, NULL, 'f'},
{"font_size", required_argument, NULL, 'F'},
- {"password_character", required_argument, NULL, 'c'},
+ {"password_character", required_argument, NULL, 'C'},
+ {"command", required_argument, NULL, 'c'},
+ {"username", required_argument, NULL, 'u'},
{"width_characters", required_argument, NULL, 'n'},
{"wide_layout", no_argument, NULL, 'w'},
{NULL, 0, NULL, 0}
};
- const char *short_options = "b:B:e:E:f:F:r:R:n:o:O:c:T:w";
+ const char *short_options = "b:B:c:C:e:E:f:F:r:R:n:o:O:T:u:w";
int opt = getopt_long(argc, argv, short_options, long_options, NULL);
while (opt != -1) {
@@ -539,16 +561,23 @@ int main(int argc, char *argv[])
state.window.entry.font_size =
strtol(optarg, NULL, 0);
break;
- case 'c':
+ case 'C':
mbrtowc(
&state.window.entry.password_character,
optarg,
4,
NULL);
break;
+ case 'c':
+ state.command = optarg;
+ break;
+ case 'u':
+ state.username = optarg;
+ break;
case 'n':
state.window.entry.num_characters =
strtol(optarg, NULL, 0);
+ break;
case 'w':
state.window.entry.tight_layout = false;
break;
@@ -724,6 +753,8 @@ int main(int argc, char *argv[])
state.window.surface.redraw = false;
state.window.entry.surface.redraw = false;
+ create_session(&state);
+
while (wl_display_dispatch(state.wl_display) != -1) {
if (state.closed) {
break;
@@ -746,6 +777,10 @@ int main(int argc, char *argv[])
&state.window.entry.image);
state.window.entry.surface.redraw = false;
}
+ if (state.submit) {
+ post_auth_message_response(&state);
+ state.submit = false;
+ }
}
log_info("Window closed, performing cleanup.\n");
@@ -754,3 +789,113 @@ int main(int argc, char *argv[])
log_info("Finished, exiting.\n");
return 0;
}
+
+void handle_response(
+ struct client_state *state,
+ struct json_object *response,
+ enum greetd_request_type request)
+{
+ if (response == NULL) {
+ return;
+ }
+ enum greetd_response_type type = greetd_parse_response_type(response);
+
+ switch (type) {
+ case GREETD_RESPONSE_SUCCESS:
+ switch (request) {
+ case GREETD_REQUEST_CREATE_SESSION:
+ case GREETD_REQUEST_POST_AUTH_MESSAGE_RESPONSE:
+ start_session(state);
+ break;
+ case GREETD_REQUEST_START_SESSION:
+ exit(EXIT_SUCCESS);
+ break;
+ case GREETD_REQUEST_CANCEL_SESSION:
+ break;
+ }
+ break;
+ case GREETD_RESPONSE_ERROR:
+ switch (request) {
+ case GREETD_REQUEST_POST_AUTH_MESSAGE_RESPONSE:
+ case GREETD_REQUEST_START_SESSION:
+ log_error(
+ "Failed to create greetd session: %s\n",
+ json_object_get_string(
+ json_object_object_get(
+ response,
+ "description")
+ )
+ );
+ restart_session(state);
+ break;
+ case GREETD_REQUEST_CREATE_SESSION:
+ log_error("Failed to connect to greetd session.\n");
+ break;
+ case GREETD_REQUEST_CANCEL_SESSION:
+ break;
+ }
+ break;
+ case GREETD_RESPONSE_AUTH_MESSAGE:
+ switch (greetd_parse_auth_message_type(response)) {
+ case GREETD_AUTH_MESSAGE_VISIBLE:
+ state->window.entry.password_visible = true;
+ break;
+ case GREETD_AUTH_MESSAGE_SECRET:
+ state->window.entry.password_visible = false;
+ break;
+ case GREETD_AUTH_MESSAGE_INFO:
+ case GREETD_AUTH_MESSAGE_ERROR:
+ /* TODO */
+ restart_session(state);
+ break;
+ case GREETD_AUTH_MESSAGE_INVALID:
+ break;
+ }
+ break;
+ case GREETD_RESPONSE_INVALID:
+ break;
+ }
+ json_object_put(response);
+}
+
+void create_session(struct client_state *state)
+{
+ log_debug("Creating greetd session for user '%s'.\n", state->username);
+ handle_response(
+ state,
+ greetd_create_session(state->username),
+ GREETD_REQUEST_CREATE_SESSION);
+}
+
+void start_session(struct client_state *state)
+{
+ log_debug("Starting session with command '%s'.\n", state->command);
+ handle_response(
+ state,
+ greetd_start_session(state->command),
+ GREETD_REQUEST_START_SESSION);
+}
+
+void post_auth_message_response(struct client_state *state)
+{
+ log_debug("Posting auth message response.\n");
+ handle_response(
+ state,
+ greetd_post_auth_message_response(state->window.entry.password_mb),
+ GREETD_REQUEST_POST_AUTH_MESSAGE_RESPONSE);
+}
+
+void cancel_session(struct client_state *state)
+{
+ log_debug("Cancelling session.\n");
+ handle_response(
+ state,
+ greetd_cancel_session(),
+ GREETD_REQUEST_CANCEL_SESSION);
+}
+
+void restart_session(struct client_state *state)
+{
+ cancel_session(state);
+ create_session(state);
+}