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

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