2019-11-01 20:39:34 +01:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <stdbool.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <getopt.h>
|
|
|
|
|
#include <signal.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <linux/un.h>
|
|
|
|
|
|
|
|
|
|
#define LOG_MODULE "foot-client"
|
|
|
|
|
#include "log.h"
|
|
|
|
|
#include "version.h"
|
|
|
|
|
|
|
|
|
|
static volatile sig_atomic_t aborted = 0;
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
sig_handler(int signo)
|
|
|
|
|
{
|
|
|
|
|
aborted = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_usage(const char *prog_name)
|
|
|
|
|
{
|
|
|
|
|
printf("Usage: %s [OPTIONS]...\n", prog_name);
|
|
|
|
|
printf("\n");
|
|
|
|
|
printf("Options:\n");
|
2019-11-01 21:10:47 +01:00
|
|
|
printf(" -t,--term=TERM value to set the environment variable TERM to (foot)\n"
|
|
|
|
|
" -v,--version show the version number and quit\n");
|
2019-11-01 20:39:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
main(int argc, char *const *argv)
|
|
|
|
|
{
|
|
|
|
|
int ret = EXIT_FAILURE;
|
|
|
|
|
|
|
|
|
|
const char *const prog_name = argv[0];
|
|
|
|
|
|
2019-11-01 20:50:00 +01:00
|
|
|
static const struct option longopts[] = {
|
2019-11-01 21:10:47 +01:00
|
|
|
{"term", required_argument, 0, 't'},
|
2019-11-01 20:50:00 +01:00
|
|
|
{"version", no_argument, 0, 'v'},
|
|
|
|
|
{"help", no_argument, 0, 'h'},
|
|
|
|
|
{NULL, no_argument, 0, 0},
|
|
|
|
|
};
|
|
|
|
|
|
2019-11-01 21:10:47 +01:00
|
|
|
const char *term = "";
|
|
|
|
|
|
2019-11-01 20:39:34 +01:00
|
|
|
while (true) {
|
2019-11-01 21:10:47 +01:00
|
|
|
int c = getopt_long(argc, argv, ":t:hv", longopts, NULL);
|
2019-11-01 20:39:34 +01:00
|
|
|
if (c == -1)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
switch (c) {
|
2019-11-01 21:10:47 +01:00
|
|
|
case 't':
|
|
|
|
|
term = optarg;
|
|
|
|
|
break;
|
|
|
|
|
|
2019-11-01 20:39:34 +01:00
|
|
|
case 'v':
|
|
|
|
|
printf("footclient version %s\n", FOOT_VERSION);
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
|
|
|
|
|
|
case 'h':
|
|
|
|
|
print_usage(prog_name);
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
|
|
|
|
|
|
case ':':
|
|
|
|
|
fprintf(stderr, "error: -%c: missing required argument\n", optopt);
|
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
|
|
|
|
|
case '?':
|
|
|
|
|
fprintf(stderr, "error: -%c: invalid option\n", optopt);
|
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
argc -= optind;
|
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
|
|
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
|
|
|
if (fd == -1) {
|
|
|
|
|
LOG_ERRNO("failed to create socket");
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool connected = false;
|
|
|
|
|
struct sockaddr_un addr = {.sun_family = AF_UNIX};
|
|
|
|
|
|
|
|
|
|
const char *xdg_runtime = getenv("XDG_RUNTIME_DIR");
|
|
|
|
|
if (xdg_runtime != NULL) {
|
|
|
|
|
snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/foot.sock", xdg_runtime);
|
|
|
|
|
|
|
|
|
|
if (connect(fd, (const struct sockaddr *)&addr, sizeof(addr)) == 0)
|
|
|
|
|
connected = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!connected) {
|
|
|
|
|
strncpy(addr.sun_path, "/tmp/foot.sock", sizeof(addr.sun_path) - 1);
|
|
|
|
|
if (connect(fd, (const struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
|
|
|
|
LOG_ERRNO("failed to connect (is 'foot --server' running?)");
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-01 21:10:47 +01:00
|
|
|
uint16_t term_len = strlen(term);
|
|
|
|
|
if (send(fd, &term_len, sizeof(term_len), 0) != sizeof(term_len) ||
|
|
|
|
|
send(fd, term, term_len, 0) != term_len)
|
|
|
|
|
{
|
|
|
|
|
LOG_ERRNO("failed to send TERM to server");
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-01 20:39:34 +01:00
|
|
|
if (send(fd, &argc, sizeof(argc), 0) != sizeof(argc)) {
|
|
|
|
|
LOG_ERRNO("failed to send argc/argv to server");
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < argc; i++) {
|
|
|
|
|
uint16_t len = strlen(argv[i]);
|
|
|
|
|
|
|
|
|
|
if (send(fd, &len, sizeof(len), 0) != sizeof(len) ||
|
|
|
|
|
send(fd, argv[i], len, 0) != sizeof(len))
|
|
|
|
|
{
|
|
|
|
|
LOG_ERRNO("failed to send argc/argv to server");
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-01 21:13:37 +01:00
|
|
|
const struct sigaction sa = {.sa_handler = &sig_handler};
|
|
|
|
|
if (sigaction(SIGINT, &sa, NULL) < 0 || sigaction(SIGTERM, &sa, NULL) < 0) {
|
2019-11-01 20:39:34 +01:00
|
|
|
LOG_ERRNO("failed to register signal handlers");
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int exit_code;
|
|
|
|
|
ssize_t rcvd = recv(fd, &exit_code, sizeof(exit_code), 0);
|
|
|
|
|
|
|
|
|
|
if (rcvd == -1 && errno == EINTR)
|
|
|
|
|
assert(aborted);
|
|
|
|
|
else if (rcvd != sizeof(exit_code))
|
|
|
|
|
LOG_ERRNO("failed to read server response");
|
|
|
|
|
else
|
|
|
|
|
ret = exit_code;
|
|
|
|
|
|
|
|
|
|
err:
|
|
|
|
|
if (fd != -1)
|
|
|
|
|
close(fd);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|