123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- #include <asm-generic/ioctls.h>
- #define _GNU_SOURCE
- #include <libnotify/notification.h>
- #include <libnotify/notify.h>
- #include <glib-2.0/glib.h>
- #include <glib-2.0/glib-unix.h>
- #include <string.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <errno.h>
- #include <stdio.h>
- #include <fcntl.h>
- #include <signal.h>
- #include <sys/wait.h>
- #include <sys/ioctl.h>
- #include <getopt.h>
- #include "term.h"
- #define READ_BUF_SIZE 4096
- #ifdef DEBUG
- #define LOG(args...) do {printf("[qcmd] "); printf(args); printf("\n");} while(0)
- #else
- #define LOG(args...) do {} while(0)
- #endif
- struct qcmd {
- GMainLoop *loop;
- NotifyNotification *not;
- int cmd_pid;
- int cmd_fd;
- char *cmd;
- int close_on_exit;
- struct term terminal;
- int pango;
- };
- struct qcmd state = {0};
- static void clean_up()
- {
- g_object_unref(G_OBJECT(state.not));
- notify_uninit();
- close(state.cmd_fd);
- }
- void cmd_exit()
- {
- LOG("cmd_exit()");
- int status;
- pid_t pid = wait(&status);
- if (pid > 0) {
- if (WIFEXITED(status)) {
- if (WEXITSTATUS(status) != 0) {
- LOG("setting urgency to critical ...");
- notify_notification_set_urgency(state.not, NOTIFY_URGENCY_CRITICAL);
- LOG("urgency set");
- }
- }
- }
- if (state.close_on_exit) {
- notify_notification_close(state.not, NULL);
- }
- }
- static void exit_qcmd() {
- kill(state.cmd_pid, SIGKILL);
- cmd_exit();
- g_main_loop_quit(state.loop);
- }
- static void not_closed_callback(gpointer data)
- {
- LOG("not_closed_callback()");
- exit_qcmd();
- }
- static gboolean cmd_read_callback(gint fd, GIOCondition condition, gpointer user_data)
- {
- char read_buf[READ_BUF_SIZE];
- char *output;
- if (condition == G_IO_HUP) {
- return FALSE;
- }
- int size = 0;
- if ((size = read(fd, read_buf, READ_BUF_SIZE - 1)) > 0) {
- LOG("read %d bytes from pipe", size);
- read_buf[size] = '\0';
- term_put_string(&state.terminal, read_buf);
- }
- output = term_to_string(&state.terminal);
- notify_notification_update(state.not, state.cmd, output, NULL);
- notify_notification_show(state.not, NULL);
- free(output);
- return TRUE;
- }
- static int cmd_open(char *cmd, int *pid) {
- int pt = posix_openpt(O_RDWR | O_NOCTTY);
- int pts_fl = fcntl(pt, F_GETFL);
- if (-1 == pts_fl) {
- exit(1);
- }
- if (-1 == fcntl(pt, F_SETFL, pts_fl | O_NONBLOCK)) {
- exit(1);
- }
- struct winsize winsz = {
- .ws_row = state.terminal.num_rows,
- .ws_col = state.terminal.num_cols,
- };
- if (-1 == ioctl(pt, TIOCSWINSZ, &winsz)) {
- fprintf(stderr, "error: ioctl(): %s\n", strerror(errno));
- exit(1);
- }
- int p = fork();
- switch (p) {
- case -1:
- LOG("Error forking process.");
- exit(1);
- case 0:
- if (-1 == grantpt(pt)) {
- exit(1);
- }
- if (-1 == unlockpt(pt)) {
- exit(1);
- }
- char *pts_path = ptsname(pt);
- if (NULL == pts_path) {
- exit(1);
- }
- close(pt);
- if (-1 == setsid()) {
- fprintf(stderr, "error: setsid(): %s\n", strerror(errno));
- exit(1);
- }
- LOG("Opening terminal slave '%s'", pts_path);
- int pts_fd = open(pts_path, O_RDWR);
- if (-1 == pts_fd) {
- exit(1);
- }
- if (-1 == ioctl(pts_fd, TIOCSCTTY, 0)) {
- fprintf(stderr, "error: ioctl(): %s\n", strerror(errno));
- exit(1);
- }
- int pts_fl = fcntl(pts_fd, F_GETFL);
- if (-1 == pts_fl) {
- exit(1);
- }
- if (-1 == fcntl(pts_fd, F_SETFL, pts_fl | O_NONBLOCK)) {
- exit(1);
- }
- dup2(pts_fd, 0);
- dup2(pts_fd, 1);
- dup2(pts_fd, 2);
- close(pts_fd);
- execl("/bin/sh", "sh", "-c", cmd, (char *) NULL);
- exit(1);
- default:
- *pid = p;
- return pt;
- }
- }
- void print_version()
- {
- #ifdef VERSION
- printf("qcmd version %s\n", VERSION);
- #else
- printf("qcmd (unknown version)\n");
- #endif
- }
- void print_help()
- {
- printf(
- "usage: qcmd [options] [command]\n"
- " -h, --help Display this help text.\n"
- " -v, --version Display version information.\n"
- " -r Number of rows in terminal output.\n"
- " -c Number of columns in terminal output.\n"
- " -C, --close-on-exit Close the notification on command exit.\n"
- " -p, --pango Output as pango markup.\n"
- );
- }
- int main(int argc, char *argv[])
- {
- char *cmd;
- int rows = 10;
- int cols = 80;
- int c;
- int option_index = 0;
- int cmd_pid;
- int cmd_fd;
- while (1) {
- static struct option long_options[] = {
- {"help", no_argument, 0, 'h'},
- {"version", no_argument, 0, 'v'},
- {"close-on-exit", no_argument, 0, 'C'},
- {"pango", no_argument, 0, 'p'},
- {0, 0, 0, 0},
- };
- c = getopt_long(argc, argv, "hvCpc:r:", long_options, &option_index);
- if (c == -1) {
- break;
- }
- switch (c) {
- case 'h':
- print_help();
- return EXIT_SUCCESS;
- case 'v':
- print_version();
- return EXIT_SUCCESS;
- case 'C':
- state.close_on_exit = 1;
- break;
- case 'r':
- rows = atoi(optarg);
- break;
- case 'c':
- cols = atoi(optarg);
- break;
- case 'p':
- state.pango = 1;
- }
- }
- if (optind < argc) {
- cmd = argv[optind];
- }
- if (cmd == NULL || strlen(cmd) == 0) {
- printf("error: no command specified.\n");
- print_help();
- return EXIT_FAILURE;
- }
- if (rows < 1 || rows > 100) {
- printf("error: invalid value %u for argument rows.\n", rows);
- return EXIT_FAILURE;
- }
- if (cols < 1 || cols > 100) {
- printf("error: invalid value %u for argument cols.\n", cols);
- return EXIT_FAILURE;
- }
- term_init(&state.terminal, rows, cols, state.pango);
- signal(SIGCHLD, cmd_exit);
- LOG("Forking off command '%s'...", cmd);
- cmd_fd = cmd_open(cmd, &cmd_pid);
- if (-1 == cmd_fd) {
- LOG("Error running command.");
- return EXIT_FAILURE;
- }
- state.cmd_fd = cmd_fd;
- state.cmd = cmd;
- LOG("Process started with PID %u.", cmd_pid);
- state.cmd_pid = cmd_pid;
- notify_init("qcmd");
- NotifyNotification *not = notify_notification_new(cmd, " ", NULL);
- state.not = not;
- g_signal_connect(G_OBJECT(not),
- "closed",
- G_CALLBACK(not_closed_callback),
- NULL);
- notify_notification_set_timeout(not, 0);
- notify_notification_show(not, NULL);
- g_unix_fd_add(cmd_fd, G_IO_IN | G_IO_HUP | G_IO_ERR, cmd_read_callback, &state);
- state.loop = g_main_loop_new(NULL, FALSE);
- g_main_loop_run(state.loop);
- LOG("Main loop exited.");
- clean_up(&state);
- return EXIT_SUCCESS;
- }
|