aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md10
-rw-r--r--TODO2
-rw-r--r--main.c399
3 files changed, 329 insertions, 82 deletions
diff --git a/README.md b/README.md
index 2188697..b2f2515 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,9 @@
This program fixes password prompt spam in GPU Screen Recorder when used from flatpak and keep removing the password prompt spam even after updating GPU Screen Recorder flatpak.
This program is meant to be ran with flatpak-spawn --host. This program is not meant to be run by the user and is automatically launched from GPU Screen Recorder flatpak.
-When this program is first run, it executes itself with pkexec and in that case it sets CAP_SYS_ADMIN capability on itself
-after copying self to ~/.local/share/gpu-screen-recorder and launches gsr-kms-server (in flatpak location).
-When this program is run after that it will check if its run from ~/.local/share/gpu-screen-recorder or launch that and it will see that the program has CAP_SYS_ADMIN capability
-and will launch gsr-kms-server (in flatpak location). This program also sets CAP_SYS_ADMIN on gsr-kms-server in the flatpak app directory.
-
-Fallbacks are used. For example if it fails to create the file in ~/.local/share/gpu-screen-recorder and set capability on that then the gsr-kms-server is used directly,
-which will keep its capabilities until GPU Screen Recorder flatpak is updated. If setting capabilities on gsr-kms-server also fails then gsr-kms-server is launched with pkexec,
-so a password prompt will show up.
The reason all of this is needed is because `setcap cap_sys_admin+ep gsr-kms-server` can't be done in the flatpak because of sandboxing so this is only done when you install GPU Screen Recorder
from source/aur to workaround that limitation.
-This program also installs gsr-global-hotkeys from the GPU Screen Recorder flatpak to ~/.local/share/gpu-screen-recorder.
+This program also launches gsr-global-hotkeys from the GPU Screen Recorder flatpak with root privileges.
The depends directory contains libcap from the libcap project, available here: https://mirrors.edge.kernel.org/pub/linux/libs/security/linux-privs/libcap2/. The version used is libcap-2.69. \ No newline at end of file
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..1dcd973
--- /dev/null
+++ b/TODO
@@ -0,0 +1,2 @@
+Instead of checking if $HOME and / has permissions, check if ~/.local/share/gpu-screen-recorder has permission by going up the tree,
+ checking ~/.local/share/gpu-screen-recorder and then ~/.local/share and so on, until /. \ No newline at end of file
diff --git a/main.c b/main.c
index 23b6104..ff71eed 100644
--- a/main.c
+++ b/main.c
@@ -5,52 +5,61 @@
#include <limits.h>
#include <errno.h>
#include <fcntl.h>
+#include <stdbool.h>
+#include <stdint.h>
#include <sys/stat.h>
-#include <sys/sendfile.h>
#include <sys/capability.h>
+#define KMS_SERVER_PROXY_FILEPATH "/var/lib/flatpak/app/com.dec05eba.gpu_screen_recorder/current/active/files/bin/kms-server-proxy"
#define GSR_KMS_SERVER_FILEPATH "/var/lib/flatpak/app/com.dec05eba.gpu_screen_recorder/current/active/files/bin/gsr-kms-server"
#define GSR_GLOBAL_HOTKEYS_FILEPATH "/var/lib/flatpak/app/com.dec05eba.gpu_screen_recorder/current/active/files/bin/gsr-global-hotkeys"
-static int readlink_realpath(const char *filepath, char *buffer) {
+static bool readlink_realpath(const char *filepath, char *buffer) {
char symlinked_path[PATH_MAX];
ssize_t bytes_written = readlink(filepath, symlinked_path, sizeof(symlinked_path) - 1);
if(bytes_written == -1 && errno == EINVAL) {
/* Not a symlink */
strncpy(symlinked_path, filepath, sizeof(symlinked_path));
} else if(bytes_written == -1) {
- return 0;
+ return false;
} else {
symlinked_path[bytes_written] = '\0';
}
if(!realpath(symlinked_path, buffer))
- return 0;
+ return false;
- return 1;
+ return true;
}
-static int file_has_sys_admin_capability(const char *filepath) {
+static bool file_has_capabilities(const char *filepath, const cap_value_t *caps, int num_caps) {
cap_t cap = cap_get_file(filepath);
if(!cap)
- return 0;
+ return false;
+
+ bool has_caps = true;
+ for(int i = 0; i < num_caps; ++i) {
+ cap_flag_value_t res = CAP_CLEAR;
+ cap_get_flag(cap, caps[i], CAP_PERMITTED, &res);
+ if(res != CAP_SET) {
+ has_caps = false;
+ break;
+ }
+ }
- cap_flag_value_t res = CAP_CLEAR;
- cap_get_flag(cap, CAP_SYS_ADMIN, CAP_PERMITTED, &res);
- int cap_set = res == CAP_SET;
cap_free(cap);
- return cap_set;
+ return has_caps;
}
-static int file_set_capabilities(const char *filepath, const cap_value_t *caps_to_set, int num_caps_to_set) {
+static bool file_set_capabilities(const char *filepath, const cap_value_t *caps_to_set, int num_caps_to_set) {
cap_t cap = cap_get_file(filepath);
if(!cap && errno != ENODATA)
- return 0;
+ return false;
if(!cap) {
cap = cap_init();
if(!cap)
- return 0;
+ return false;
}
int res = 0;
@@ -62,7 +71,90 @@ static int file_set_capabilities(const char *filepath, const cap_value_t *caps_t
return res == 0;
}
-static int create_local_kms_server_proxy_directory(const char *home) {
+static const char* get_column(const char *str, int size, int *column_size, int column) {
+ *column_size = 0;
+ int current_column = 0;
+ int i = 0;
+ for(; column > 0 && i < size; ++i) {
+ if(str[i] == ' ') {
+ ++current_column;
+ if(current_column == column) {
+ ++i;
+ break;
+ }
+ }
+ }
+
+ const int column_start = i;
+ for(; i < size; ++i) {
+ if(str[i] == ' ') {
+ *column_size = i - column_start;
+ return str + column_start;
+ }
+ }
+
+ *column_size = size;
+ return str;
+}
+
+static bool mount_has_flag(const char *flags, int flags_size, const char *flag_to_find) {
+ if(flags_size == 0)
+ return false;
+
+ const int flag_to_find_size = strlen(flag_to_find);
+ int offset = 0;
+ for(;;) {
+ const char *p = memmem(flags + offset, flags_size - offset, flag_to_find, flag_to_find_size);
+ if(!p)
+ return false;
+
+ offset = p - flags;
+ const char prev_char = offset > 0 ? flags[offset - 1] : ',';
+ const char next_char = offset + flag_to_find_size < flags_size ? flags[offset + flag_to_find_size] : ',';
+ if((prev_char == ',' || prev_char == '\n') && (next_char == ',' || next_char == '\n'))
+ return true;
+ }
+
+ return false;
+}
+
+static bool mount_has_setuid_permission(const char *path) {
+ FILE *f = fopen("/proc/mounts", "rb");
+ if(!f) {
+ fprintf(stderr, "Error: failed to open /proc/mounts\n");
+ // Assume it's ok and just continue
+ return true;
+ }
+
+ // Assume there is permission if it can't be found
+ bool has_permission = true;
+ const int path_size = strlen(path);
+ char line[1024];
+ while(fgets(line, sizeof(line), f)) {
+ const int line_size = strlen(line);
+ int mount_point_size = 0;
+ const char *mount_point = get_column(line, line_size, &mount_point_size, 1);
+ if(!mount_point)
+ continue;
+
+ if(mount_point && mount_point_size == path_size && memcmp(mount_point, path, mount_point_size) == 0) {
+ int flags_size = 0;
+ const char *flags = get_column(line, line_size, &flags_size, 3);
+ if(!flags)
+ continue;
+
+ const bool has_rw = mount_has_flag(flags, flags_size, "rw");
+ const bool has_nosuid = mount_has_flag(flags, flags_size, "nosuid");
+ has_permission = has_rw && !has_nosuid;
+ break;
+ }
+ }
+
+ fclose(f);
+ return has_permission;
+}
+
+static bool create_local_kms_server_proxy_directory(const char *home) {
char path[PATH_MAX];
int err;
@@ -72,35 +164,53 @@ static int create_local_kms_server_proxy_directory(const char *home) {
snprintf(path, sizeof(path), "%s/%s", home, path_part);
err = mkdir(path, S_IRWXU);
if(err == -1 && errno != EEXIST)
- return 0;
+ return false;
}
- return 1;
+ return true;
}
-static int copy_file_atomic_set_capabilities(const char *source_path, const char *dest_path, const cap_value_t *caps_to_set, int num_caps_to_set) {
+static bool copy_file_atomic(const char *source_path, const char *dest_path) {
int in_fd = -1;
int out_fd = -1;
- int res = 0;
+ bool res = false;
char tmp_filepath[PATH_MAX];
snprintf(tmp_filepath, sizeof(tmp_filepath), "%s.tmp", dest_path);
in_fd = open(source_path, O_RDONLY);
- if(in_fd == -1)
+ if(in_fd == -1) {
+ fprintf(stderr, "Error: copy_file_atomic: failed to open %s, error: %s\n", source_path, strerror(errno));
goto done;
+ }
- struct stat st;
- if(fstat(in_fd, &st) == -1)
+ out_fd = open(tmp_filepath, O_WRONLY | O_CREAT, 0755);
+ if(out_fd == -1) {
+ fprintf(stderr, "Error: copy_file_atomic: failed to create %s, error: %s\n", tmp_filepath, strerror(errno));
goto done;
+ }
- out_fd = open(tmp_filepath, O_RDWR | O_CREAT | O_TRUNC, 0755);
- if(out_fd == -1)
- goto done;
+ uint8_t buffer[8192];
+ for(;;) {
+ const ssize_t bytes_read = read(in_fd, buffer, sizeof(buffer));
+ if(bytes_read == -1) {
+ fprintf(stderr, "Error: copy_file_atomic: failed to read data from %s, error: %s\n", source_path, strerror(errno));
+ goto done;
+ } else if(bytes_read == 0) {
+ break;
+ }
- if(sendfile(out_fd, in_fd, NULL, st.st_size) != st.st_size)
- goto done;
+ ssize_t bytes_written_total = 0;
+ while(bytes_written_total < bytes_read) {
+ const ssize_t bytes_written = write(out_fd, buffer + bytes_written_total, bytes_read - bytes_written_total);
+ if(bytes_written == -1) {
+ fprintf(stderr, "Error: copy_file_atomic: failed to write data to %s, error: %s\n", tmp_filepath, strerror(errno));
+ goto done;
+ }
+ bytes_written_total += bytes_written;
+ }
+ }
- res = 1;
+ res = true;
done:
if(in_fd)
@@ -108,28 +218,67 @@ static int copy_file_atomic_set_capabilities(const char *source_path, const char
if(out_fd)
close(out_fd);
- if(res)
- res = file_set_capabilities(tmp_filepath, caps_to_set, num_caps_to_set);
-
- if(res)
+ if(res) {
res = rename(tmp_filepath, dest_path) == 0;
+ if(!res)
+ fprintf(stderr, "Error: copy_file_atomic: Failed to rename from %s to %s, error: %s\n", strerror(errno), tmp_filepath, dest_path);
+ } else {
+ remove(tmp_filepath);
+ }
return res;
}
-static void usage(void) {
- fprintf(stderr, "usage alt.1: kms-server-proxy <initial_socket_path> <card_path> <user_homepath>\n");
- fprintf(stderr, "usage alt.2: kms-server-proxy setup-gsr-ui [user_homepath]\n");
- exit(1);
+static bool gsr_files_set_permissions_and_capabilities(const char *kms_server_proxy_local_filepath) {
+ /* owner: read/write/execute, group: read/execute, public: read/execute */
+ if(chmod(kms_server_proxy_local_filepath, 0755) != 0) {
+ fprintf(stderr, "Error: failed to set %s permissions\n", kms_server_proxy_local_filepath);
+ return false;
+ }
+
+ if(chown(kms_server_proxy_local_filepath, 0, 0) != 0) {
+ fprintf(stderr, "Error: failed to set %s ownership\n", kms_server_proxy_local_filepath);
+ return false;
+ }
+
+ if(!file_set_capabilities(kms_server_proxy_local_filepath, (const cap_value_t[]){ CAP_SYS_ADMIN, CAP_SETUID }, 2)) {
+ fprintf(stderr, "Error: failed to set kms-server-proxy capabilities\n");
+ return false;
+ }
+
+ return true;
}
-static int setup_gsr_ui(const char *user_homepath) {
- char self_path[PATH_MAX];
- if(!readlink_realpath("/proc/self/exe", self_path)) {
- fprintf(stderr, "failed to resolve /proc/self/exe\n");
- return 1;
+/* |gsr_global_hotkeys_local_filepath| can be NULL */
+static bool setup_local_gsr_files(const char *user_homepath, const char *kms_server_proxy_home) {
+ if(!create_local_kms_server_proxy_directory(user_homepath)) {
+ fprintf(stderr, "Error: failed to create ~/.local/share/gpu-screen-recorder directory\n");
+ return false;
+ }
+
+ remove(kms_server_proxy_home);
+ if(!copy_file_atomic(KMS_SERVER_PROXY_FILEPATH, kms_server_proxy_home)) {
+ fprintf(stderr, "Error: failed to copy kms-server-proxy to %s\n", kms_server_proxy_home);
+ return false;
}
+ return true;
+}
+
+static bool get_local_kms_server_proxy_filepath(char *output, size_t output_size, const char *user_homepath) {
+ if(mount_has_setuid_permission(user_homepath)) {
+ snprintf(output, output_size, "%s/.local/share/gpu-screen-recorder/kms-server-proxy-2", user_homepath);
+ return true;
+ } else if(mount_has_setuid_permission("/")) {
+ snprintf(output, output_size, "/usr/bin/kms-server-proxy-2");
+ return true;
+ } else {
+ fprintf(stderr, "Error: neither %s nor / has both read-write permission and setuid permission\n", user_homepath);
+ return false;
+ }
+}
+
+static int setup_gsr_ui(const char *user_homepath) {
if(!user_homepath) {
user_homepath = getenv("HOME");
if(!user_homepath)
@@ -137,24 +286,54 @@ static int setup_gsr_ui(const char *user_homepath) {
}
char kms_server_proxy_local_filepath[PATH_MAX];
- /* Update kms-server-proxy-N to kms-server-proxy-N+1 on update (update that needs to run before this program launches itself) */
- snprintf(kms_server_proxy_local_filepath, sizeof(kms_server_proxy_local_filepath), "%s/.local/share/gpu-screen-recorder/kms-server-proxy-1", user_homepath);
+ if(!get_local_kms_server_proxy_filepath(kms_server_proxy_local_filepath, sizeof(kms_server_proxy_local_filepath), user_homepath))
+ return 1;
- char gsr_global_hotkeys_local_filepath[PATH_MAX];
- snprintf(gsr_global_hotkeys_local_filepath, sizeof(gsr_global_hotkeys_local_filepath), "%s/.local/share/gpu-screen-recorder/gsr-global-hotkeys", user_homepath);
+ /* Weird-ass distros like openSUSE only allow pkexec for files in $HOME!!! */
+ char kms_server_proxy_home[PATH_MAX];
+ snprintf(kms_server_proxy_home, sizeof(kms_server_proxy_home), "%s/kms-server-proxy", user_homepath);
if(geteuid() == 0) { /* is current user root? */
- int success = 1;
- success &= (file_set_capabilities(GSR_KMS_SERVER_FILEPATH, (const cap_value_t[]){ CAP_SYS_ADMIN }, 1) == 1);
- success &= (copy_file_atomic_set_capabilities(self_path, kms_server_proxy_local_filepath, (const cap_value_t[]){ CAP_SYS_ADMIN, CAP_SETFCAP }, 2) == 1);
- success &= (copy_file_atomic_set_capabilities(GSR_GLOBAL_HOTKEYS_FILEPATH, gsr_global_hotkeys_local_filepath, (const cap_value_t[]){ CAP_SETUID }, 1) == 1);
- return success ? 0 : 1;
+ remove(kms_server_proxy_home);
+ remove(kms_server_proxy_local_filepath);
+ if(!copy_file_atomic(KMS_SERVER_PROXY_FILEPATH, kms_server_proxy_local_filepath)) {
+ fprintf(stderr, "Error: failed to copy kms-server-proxy to %s\n", kms_server_proxy_local_filepath);
+ return 1;
+ }
+
+ if(!gsr_files_set_permissions_and_capabilities(kms_server_proxy_local_filepath))
+ return 1;
+
+ return 0;
} else {
- create_local_kms_server_proxy_directory(user_homepath);
+ if(!setup_local_gsr_files(user_homepath, kms_server_proxy_home))
+ return 1;
- const char *args[] = { "pkexec", self_path, "setup-gsr-ui", user_homepath, NULL };
- return execvp(args[0], (char *const*)args);
+ const char *args[] = { "pkexec", kms_server_proxy_home, "setup-gsr-ui", user_homepath, NULL };
+ const int result = execvp(args[0], (char *const*)args);
+ perror("pkexec");
+ remove(kms_server_proxy_home);
+ return result;
+ }
+}
+
+static bool is_setup(const char *user_homepath) {
+ if(!user_homepath) {
+ user_homepath = getenv("HOME");
+ if(!user_homepath)
+ user_homepath = "/tmp";
+ }
+
+ char kms_server_proxy_local_filepath[PATH_MAX];
+ if(!get_local_kms_server_proxy_filepath(kms_server_proxy_local_filepath, sizeof(kms_server_proxy_local_filepath), user_homepath))
+ return false;
+
+ if(!file_has_capabilities(kms_server_proxy_local_filepath, (const cap_value_t[]){ CAP_SYS_ADMIN, CAP_SETUID }, 2)) {
+ fprintf(stderr, "Error: kms-server-proxy (%s) is either not installed or is missing capabilities\n", kms_server_proxy_local_filepath);
+ return false;
}
+
+ return true;
}
static int launch_gsr_kms_server(const char *initial_socket_path, const char *card_path, const char *user_homepath) {
@@ -165,13 +344,14 @@ static int launch_gsr_kms_server(const char *initial_socket_path, const char *ca
}
char kms_server_proxy_local_filepath[PATH_MAX];
- /* Update kms-server-proxy-N to kms-server-proxy-N+1 on update (update that needs to run before this program launches itself) */
- snprintf(kms_server_proxy_local_filepath, sizeof(kms_server_proxy_local_filepath), "%s/.local/share/gpu-screen-recorder/kms-server-proxy-1", user_homepath);
+ if(!get_local_kms_server_proxy_filepath(kms_server_proxy_local_filepath, sizeof(kms_server_proxy_local_filepath), user_homepath))
+ return 1;
- if(file_has_sys_admin_capability(GSR_KMS_SERVER_FILEPATH)) {
- const char *args[] = { GSR_KMS_SERVER_FILEPATH, initial_socket_path, card_path, NULL };
- return execv(args[0], (char *const*)args);
- } else if(file_has_sys_admin_capability(kms_server_proxy_local_filepath)) {
+ /* Weird-ass distros like openSUSE only allow pkexec for files in $HOME!!! */
+ char kms_server_proxy_home[PATH_MAX];
+ snprintf(kms_server_proxy_home, sizeof(kms_server_proxy_home), "%s/kms-server-proxy", user_homepath);
+
+ if(file_has_capabilities(kms_server_proxy_local_filepath, (const cap_value_t[]){ CAP_SYS_ADMIN, CAP_SETUID }, 2)) {
/* Need to resolve kms_server_proxy_local_filepath because /home can be a symlink to another location */
char kms_server_proxy_local_filepath_full[PATH_MAX];
if(!readlink_realpath(kms_server_proxy_local_filepath, kms_server_proxy_local_filepath_full)) {
@@ -188,40 +368,113 @@ static int launch_gsr_kms_server(const char *initial_socket_path, const char *ca
return execv(args[0], (char *const*)args);
}
- /* TODO: Remove the need for this. Instead inherit capabilities */
- if(file_set_capabilities(GSR_KMS_SERVER_FILEPATH, (const cap_value_t[]){ CAP_SYS_ADMIN }, 1)) {
- const char *args[] = { GSR_KMS_SERVER_FILEPATH, initial_socket_path, card_path, NULL };
- return execv(args[0], (char *const*)args);
+ if(setuid(0) == -1) {
+ fprintf(stderr, "Error: failed to switch to root user to launch gsr-kms-server\n");
+ return 1;
}
- const char *args[] = { "pkexec", GSR_KMS_SERVER_FILEPATH, initial_socket_path, card_path, NULL };
+ const char *args[] = { GSR_KMS_SERVER_FILEPATH, initial_socket_path, card_path, NULL };
return execvp(args[0], (char *const*)args);
} else if(geteuid() == 0) { /* is current user root? */
- file_set_capabilities(GSR_KMS_SERVER_FILEPATH, (const cap_value_t[]){ CAP_SYS_ADMIN }, 1);
- copy_file_atomic_set_capabilities(self_path, kms_server_proxy_local_filepath, (const cap_value_t[]){ CAP_SYS_ADMIN, CAP_SETFCAP }, 2);
+ remove(kms_server_proxy_home);
+ remove(kms_server_proxy_local_filepath);
+ if(!copy_file_atomic(KMS_SERVER_PROXY_FILEPATH, kms_server_proxy_local_filepath)) {
+ fprintf(stderr, "Error: failed to copy kms-server-proxy to %s\n", kms_server_proxy_local_filepath);
+ return 1;
+ }
+
+ if(!gsr_files_set_permissions_and_capabilities(kms_server_proxy_local_filepath))
+ return 1;
const char *args[] = { GSR_KMS_SERVER_FILEPATH, initial_socket_path, card_path, NULL };
return execv(args[0], (char *const*)args);
} else {
- create_local_kms_server_proxy_directory(user_homepath);
+ if(!setup_local_gsr_files(user_homepath, kms_server_proxy_home))
+ return 1;
- const char *args[] = { "pkexec", self_path, initial_socket_path, card_path, user_homepath, NULL };
- return execvp(args[0], (char *const*)args);
+ const char *args[] = { "pkexec", kms_server_proxy_home, initial_socket_path, card_path, user_homepath, NULL };
+ const int result = execvp(args[0], (char *const*)args);
+ perror("pkexec");
+ remove(kms_server_proxy_home);
+ return result;
}
}
+static int launch_gsr_global_hotkeys(char **argv) {
+ char self_path[PATH_MAX];
+ if(!readlink_realpath("/proc/self/exe", self_path)) {
+ fprintf(stderr, "failed to resolve /proc/self/exe\n");
+ return 1;
+ }
+
+ const char *user_homepath = argv[2];
+
+ char kms_server_proxy_local_filepath[PATH_MAX];
+ if(!get_local_kms_server_proxy_filepath(kms_server_proxy_local_filepath, sizeof(kms_server_proxy_local_filepath), user_homepath))
+ return 1;
+
+ if(!file_has_capabilities(kms_server_proxy_local_filepath, (const cap_value_t[]){ CAP_SETUID }, 1)) {
+ fprintf(stderr, "Error: kms-server-proxy (%s) is either not installed or is missing capabilities\n", kms_server_proxy_local_filepath);
+ return 1;
+ }
+
+ /* Need to resolve kms_server_proxy_local_filepath because /home can be a symlink to another location */
+ char kms_server_proxy_local_filepath_full[PATH_MAX];
+ if(!readlink_realpath(kms_server_proxy_local_filepath, kms_server_proxy_local_filepath_full)) {
+ fprintf(stderr, "failed to resolve %s\n", kms_server_proxy_local_filepath);
+ return 1;
+ }
+
+ /*
+ Run cached ~/.local/share/gpu-screen-recorder/kms-server-proxy which has sys admin capability.
+ The one in flatpak location gets capabilities overwritten on flatpak update.
+ */
+ if(strcmp(self_path, kms_server_proxy_local_filepath_full) != 0) {
+ argv[0] = kms_server_proxy_local_filepath_full;
+ return execv(argv[0], argv);
+ }
+
+ if(setuid(0) == -1) {
+ fprintf(stderr, "Error: failed to switch to root user to launch gsr-global-hotkeys\n");
+ return 1;
+ }
+
+ argv[2] = GSR_GLOBAL_HOTKEYS_FILEPATH;
+ const int result = execv(argv[2], argv + 2);
+ perror(argv[2]);
+ return result;
+}
+
+static void usage(void) {
+ fprintf(stderr, "usage alt.1: kms-server-proxy <initial_socket_path> <card_path> <user_homepath>\n");
+ fprintf(stderr, "usage alt.2: kms-server-proxy setup-gsr-ui [user_homepath]\n");
+ fprintf(stderr, "usage alt.3: kms-server-proxy launch-gsr-global-hotkeys <user_homepath> [args...]\n");
+ fprintf(stderr, "usage alt.4: kms-server-proxy is-setup [user_homepath]\n");
+ exit(1);
+}
+
int main(int argc, char **argv) {
- if(argc == 2 || argc == 3) {
+ if(argc < 2)
+ usage();
+
+ if(strcmp(argv[1], "setup-gsr-ui") == 0) {
const char *user_homepath = argc == 3 ? argv[2] : NULL;
- if(strcmp(argv[1], "setup-gsr-ui") == 0) {
- return setup_gsr_ui(user_homepath);
+ return setup_gsr_ui(user_homepath);
+ } else if(strcmp(argv[1], "launch-gsr-global-hotkeys") == 0) {
+ if(argc >= 3) {
+ return launch_gsr_global_hotkeys(argv);
} else {
- fprintf(stderr, "error: invalid option \"%s\".\n", argv[1]);
+ fprintf(stderr, "Error: missing user_homepath argument to launch-gsr-global-hotkeys\n");
usage();
}
+ } else if(strcmp(argv[1], "is-setup") == 0) {
+ const char *user_homepath = argc == 3 ? argv[2] : NULL;
+ const bool s = is_setup(user_homepath);
+ return s ? 0 : 1;
} else if(argc == 4) {
return launch_gsr_kms_server(argv[1], argv[2], argv[3]);
} else {
+ fprintf(stderr, "error: invalid option \"%s\".\n", argv[1]);
usage();
}
}