source: neercs/trunk/src/grab.c @ 4366

Last change on this file since 4366 was 4366, checked in by Sam Hocevar, 11 years ago

Clean up source code, copyright information, author names, SVN keywords...

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 10.6 KB
Line 
1/*
2 *  neercs        console-based window manager
3 *  Copyright (c) 2008-2010 Pascal Terjan <pterjan@linuxfr.org>
4 *                All Rights Reserved
5 *
6 *  This program is free software. It comes without any warranty, to
7 *  the extent permitted by applicable law. You can redistribute it
8 *  and/or modify it under the terms of the Do What The Fuck You Want
9 *  To Public License, Version 2, as published by Sam Hocevar. See
10 *  http://sam.zoy.org/wtfpl/COPYING for more details.
11 */
12
13#include "config.h"
14
15#define _XOPEN_SOURCE 500       /* getsid() */
16
17#include <dirent.h>
18#include <errno.h>
19#include <fcntl.h>
20#include <glob.h>
21#include <libgen.h>
22#include <signal.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <unistd.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <sys/wait.h>
30
31#if defined HAVE_LINUX_KDEV_T_H
32#   include <linux/kdev_t.h>
33#   include <linux/major.h>
34#endif
35
36#include "neercs.h"
37#include "mytrace.h"
38
39int grab_process(long pid, char *ptyname, int ptyfd, int *newpid)
40{
41#if defined HAVE_LINUX_KDEV_T_H
42    char fdstr[1024];
43    struct mytrace *parent, *child;
44    int i = 0, fd = 0, ret;
45    char to_open[128];
46    int mode[128];
47    int fds[128];
48    struct stat stat_buf;
49    struct termios tos;
50    int validtos = 0;
51    DIR *fddir;
52    struct dirent *fddirent;
53
54    debug("pty is %s", ptyname);
55
56    parent = mytrace_attach(pid);
57    if (!parent)
58    {
59        fprintf(stderr, "Cannot access process %ld\n", pid);
60        return -1;
61    }
62
63    child = mytrace_fork(parent);
64
65    snprintf(fdstr, sizeof(fdstr), "/proc/%ld/fd", pid);
66    fddir = opendir(fdstr);
67
68    /* Look for file descriptors that are PTYs */
69    while ((fddirent = readdir(fddir)) && i < (int)sizeof(to_open) - 1)
70    {
71        fd = atoi(fddirent->d_name);
72        fds[i] = fd;
73        to_open[i] = 0;
74        lstat(fdstr, &stat_buf);
75        if ((stat_buf.st_mode & S_IRUSR) && (stat_buf.st_mode & S_IWUSR))
76            mode[i] = O_RDWR;
77        else if (stat_buf.st_mode & S_IWUSR)
78            mode[i] = O_WRONLY;
79        else
80            mode[i] = O_RDONLY;
81
82        snprintf(fdstr, sizeof(fdstr), "/proc/%ld/fd/%s", pid,
83                 fddirent->d_name);
84
85        if (stat(fdstr, &stat_buf) < 0)
86            continue;
87
88        if (!S_ISCHR(stat_buf.st_mode)
89            || MAJOR(stat_buf.st_rdev) != UNIX98_PTY_SLAVE_MAJOR)
90            continue;
91
92        debug("found pty %d for pid %d", fd, pid);
93
94        if (!validtos)
95        {
96            ret = mytrace_tcgets(child, fd, &tos);
97            if (ret < 0)
98            {
99                perror("mytrace_tcgets");
100            }
101            else
102            {
103                validtos = 1;
104            }
105        }
106        to_open[i] = 1;
107        i++;
108    }
109    closedir(fddir);
110
111    if (i >= (int)sizeof(to_open) - 1)
112    {
113        fprintf(stderr, "too many open pty\n");
114        mytrace_detach(child);
115        return -1;
116    }
117
118    ret = mytrace_exec(parent, "/usr/bin/reset");
119    if (ret < 0)
120        mytrace_exit(parent, 0);
121    mytrace_detach(parent);
122    waitpid(pid, NULL, 0);      /* Wait for reset to finish before displaying */
123    mytrace_write(child, 2, "\033[H\033[2J", 7);
124    mytrace_write(child, 2, "\n[Process stolen by neercs]\r\n\n", 30);
125
126    pid = mytrace_getpid(child);
127    *newpid = pid;
128
129    /* Set the process's session ID */
130    debug("Running setsid on process %ld (sid=%d)", pid, getsid(pid));
131
132    ret = mytrace_setpgid(child, 0, getsid(pid));
133    if (ret < 0)
134    {
135        fprintf(stderr, "syscall setpgid failed\n");
136        mytrace_detach(child);
137        return -1;
138    }
139
140    if (ret != 0)
141    {
142        fprintf(stderr, "setpgid returned %d\n", ret);
143        mytrace_detach(child);
144        return -1;
145    }
146
147    ret = mytrace_setsid(child);
148    if (ret < 0)
149    {
150        fprintf(stderr, "syscall setsid failed\n");
151        mytrace_detach(child);
152        return -1;
153    }
154
155    debug("pid %ld has now sid %d", pid, getsid(pid));
156
157    /* Reopen PTY file descriptors */
158    for (; i >= 0; i--)
159    {
160        if (!to_open[i])
161            continue;
162        ret = mytrace_close(child, fds[i]);
163        if (ret < 0)
164        {
165            perror("mytrace_close");
166            continue;
167        }
168        fd = mytrace_open(child, ptyname, mode[i]);
169        if (fd < 0)
170        {
171            perror("mytrace_open");
172            continue;
173        }
174
175        /* FIXME Only needed once */
176        mytrace_sctty(child, fd);
177
178        if (validtos)
179        {
180            ret = mytrace_tcsets(child, fd, &tos);
181            if (ret < 0)
182            {
183                perror("mytrace_tcsets");
184            }
185            validtos = 0;
186        }
187        ret = mytrace_dup2(child, fd, fds[i]);
188        if (ret < 0)
189        {
190            perror("mytrace_dup2");
191        }
192    }
193
194    kill(pid, SIGWINCH);
195    mytrace_detach(child);
196
197    close(ptyfd);
198    return 0;
199#else
200    errno = ENOSYS;
201    return -1;
202#endif
203}
204
205struct process
206{
207    long pid;
208    char *cmdline;
209};
210
211static int list_process(struct process **process_list)
212{
213    glob_t pglob;
214    unsigned int i, n = 0;
215    glob("/proc/[0-9]*", GLOB_NOSORT, NULL, &pglob);
216    *process_list = malloc(pglob.gl_pathc * sizeof(struct process));
217    for (i = 0; i < pglob.gl_pathc; i++)
218    {
219        glob_t pglob2;
220        unsigned int j;
221        char *fds;
222        (*process_list)[n].pid = atoi(basename(pglob.gl_pathv[i]));
223        /* Don't allow grabbing ourselves */
224        if ((*process_list)[n].pid == getpid())
225            continue;
226        /* FIXME check value of r */
227        int r = asprintf(&fds, "%s/fd/*", pglob.gl_pathv[i]);
228        (void) r;
229        glob(fds, GLOB_NOSORT, NULL, &pglob2);
230        free(fds);
231        for (j = 0; j < pglob2.gl_pathc; j++)
232        {
233            char path[4096];
234            ssize_t l = readlink(pglob2.gl_pathv[j], path, sizeof(path));
235            if (l <= 0)
236                continue;
237            path[l] = '\0';
238            if (strstr(path, "/dev/pt"))
239            {
240                char *cmdfile;
241                int fd;
242                /* FIXME check value of r */
243                r = asprintf(&cmdfile, "%s/cmdline", pglob.gl_pathv[i]);
244                (void) r;
245                fd = open(cmdfile, O_RDONLY);
246                free(cmdfile);
247                if (fd)
248                {
249                    /* FIXME check value of r */
250                    r = read(fd, path, sizeof(path));
251                    (void) r;
252                    (*process_list)[n].cmdline = strdup(path);
253                    close(fd);
254                    n++;
255                    break;
256                }
257            }
258        }
259        globfree(&pglob2);
260    }
261    globfree(&pglob);
262    return n;
263}
264
265long select_process(struct screen_list *screen_list)
266{
267    caca_event_t ev;
268    enum caca_event_type t;
269    int current_line = 1;
270    int refresh = 1;
271    int nb_process, i;
272    int ret = 0;
273    int start = 0;
274    struct process *process_list;
275
276    nb_process = list_process(&process_list);
277
278    screen_list->cv = caca_create_canvas(0, 0);
279    screen_list->dp = caca_create_display(screen_list->cv);
280    if (!screen_list->dp)
281        goto end;
282    caca_set_cursor(screen_list->dp, 0);
283    caca_set_display_title(screen_list->dp, PACKAGE_STRING);
284    while (1)
285    {
286        if (refresh)
287        {
288            caca_set_color_ansi(screen_list->cv, CACA_BLUE, CACA_BLUE);
289            caca_fill_box(screen_list->cv,
290                          0, 0,
291                          caca_get_canvas_width(screen_list->cv),
292                          caca_get_canvas_height(screen_list->cv), '#');
293            caca_set_color_ansi(screen_list->cv, CACA_DEFAULT, CACA_BLUE);
294            caca_draw_cp437_box(screen_list->cv,
295                                0, 0,
296                                caca_get_canvas_width(screen_list->cv),
297                                caca_get_canvas_height(screen_list->cv));
298            caca_printf(screen_list->cv, 2, 2,
299                        "Please select a process to grab:");
300            for (i = 0;
301                 i < nb_process
302                 && i < caca_get_canvas_height(screen_list->cv) - 4; i++)
303            {
304                if (i == current_line - 1)
305                {
306                    caca_set_attr(screen_list->cv, CACA_BOLD);
307                    caca_set_color_ansi(screen_list->cv, CACA_GREEN,
308                                        CACA_BLUE);
309                    caca_put_char(screen_list->cv, 1, i + 3, '>');
310                }
311                else
312                {
313                    caca_set_attr(screen_list->cv, 0);
314                    caca_set_color_ansi(screen_list->cv, CACA_LIGHTGRAY,
315                                        CACA_BLUE);
316                    caca_put_char(screen_list->cv, 1, i + 3, ' ');
317                }
318                caca_printf(screen_list->cv,
319                            3, i + 3,
320                            "%5d %s",
321                            process_list[i + start].pid,
322                            process_list[i + start].cmdline);
323            }
324            caca_refresh_display(screen_list->dp);
325            refresh = 0;
326        }
327
328        if (!caca_get_event(screen_list->dp,
329                            CACA_EVENT_KEY_PRESS
330                            | CACA_EVENT_RESIZE | CACA_EVENT_QUIT, &ev, 10000))
331            continue;
332
333        t = caca_get_event_type(&ev);
334
335        if (t & CACA_EVENT_KEY_PRESS)
336        {
337            unsigned int c = caca_get_event_key_ch(&ev);
338            switch (c)
339            {
340            case CACA_KEY_UP:
341                if (current_line > 1)
342                    current_line--;
343                if (current_line < start && start > 0)
344                    start--;
345                break;
346            case CACA_KEY_DOWN:
347                if (current_line < nb_process)
348                    current_line++;
349                if (current_line >
350                    start + caca_get_canvas_height(screen_list->cv) - 3)
351                    start++;
352                break;
353            case CACA_KEY_RETURN:
354                ret = process_list[current_line - 1].pid;
355                goto end;
356                break;
357            case CACA_KEY_ESCAPE:
358                goto end;
359                break;
360            default:
361                break;
362            }
363            refresh = 1;
364        }
365        else if (t & CACA_EVENT_RESIZE)
366        {
367            refresh = 1;
368        }
369        else if (t & CACA_EVENT_QUIT)
370            goto end;
371    }
372
373  end:
374    if (screen_list->dp)
375    {
376        caca_free_display(screen_list->dp);
377        screen_list->dp = NULL;
378    }
379    if (screen_list->cv)
380    {
381        caca_free_canvas(screen_list->cv);
382        screen_list->cv = NULL;
383    }
384    if (nb_process > 0)
385    {
386        for (i = 0; i < nb_process; i++)
387        {
388            free(process_list[i].cmdline);
389        }
390        free(process_list);
391    }
392    return ret;
393}
Note: See TracBrowser for help on using the repository browser.