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

Last change on this file since 3899 was 3899, checked in by Pascal Terjan, 11 years ago
  • Apparently \033 is more common than \x1b for ESC. Use the former in our code for clarity. (following libcaca commit 3412)
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 10.2 KB
Line 
1/*
2 *  neercs        console-based window manager
3 *  Copyright (c) 2008 Pascal Terjan
4 *                All Rights Reserved
5 *
6 *  $Id: grab.c 3899 2009-11-13 16:45:48Z pterjan $
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, fddirent->d_name);
85   
86        if(stat(fdstr, &stat_buf) < 0)
87            continue;
88
89        if(!S_ISCHR(stat_buf.st_mode)
90            || MAJOR(stat_buf.st_rdev) != UNIX98_PTY_SLAVE_MAJOR)
91            continue;
92
93        debug("found pty %d for pid %d", fd, pid);
94
95        if(!validtos)
96        {
97            ret = mytrace_tcgets(child, fd, &tos);
98            if(ret < 0)
99            {
100                perror("mytrace_tcgets");
101            }
102            else
103            {
104                validtos = 1;
105            }
106        }
107        to_open[i] = 1;
108        i++;
109    }
110    closedir(fddir);
111   
112    if(i>=(int)sizeof(to_open)-1)
113    {
114        fprintf(stderr, "too many open pty\n");
115        mytrace_detach(child);
116        return -1;
117    }
118
119    ret = mytrace_exec(parent, "/usr/bin/reset");
120    if(ret < 0)
121        mytrace_exit(parent, 0);
122    mytrace_detach(parent);
123    waitpid(pid, NULL, 0); /* Wait for reset to finish before displaying */
124    mytrace_write(child, 2, "\033[H\033[2J", 7);
125    mytrace_write(child, 2, "\n[Process stolen by neercs]\r\n\n", 30);
126
127    pid = mytrace_getpid(child);
128    *newpid = pid;
129
130    /* Set the process's session ID */
131    debug("Running setsid on process %ld (sid=%d)", pid, getsid(pid));
132
133    ret = mytrace_setpgid(child, 0, getsid(pid));
134    if(ret < 0)
135    {
136        fprintf(stderr, "syscall setpgid failed\n");
137        mytrace_detach(child);
138        return -1;
139    }
140
141    if(ret != 0)
142    {
143        fprintf(stderr, "setpgid returned %d\n", ret);
144        mytrace_detach(child);
145        return -1;
146    }
147
148    ret = mytrace_setsid(child);
149    if(ret < 0)
150    {
151        fprintf(stderr, "syscall setsid failed\n");
152        mytrace_detach(child);
153        return -1;
154    }
155
156    debug("pid %ld has now sid %d", pid, getsid(pid));
157
158    /* Reopen PTY file descriptors */
159    for(; i >= 0; i--)
160    {
161        if(!to_open[i])
162            continue;
163        ret = mytrace_close(child, fds[i]);
164        if(ret < 0)
165        {
166            perror("mytrace_close");
167            continue;
168        }
169        fd = mytrace_open(child, ptyname, mode[i]);
170        if(fd < 0)
171        {
172            perror("mytrace_open");
173            continue;
174        }
175       
176        /* FIXME Only needed once */
177        mytrace_sctty(child, fd);
178
179        if(validtos)
180        {
181            ret = mytrace_tcsets(child, fd, &tos);
182            if(ret < 0)
183            {
184                perror("mytrace_tcsets");
185            }
186            validtos = 0;
187        }
188        ret = mytrace_dup2(child, fd, fds[i]);
189        if(ret < 0)
190        {
191            perror("mytrace_dup2");
192        }
193    }
194
195    kill(pid, SIGWINCH);
196    mytrace_detach(child);
197
198    close(ptyfd);
199    return 0;
200#else
201    errno = ENOSYS;
202    return -1;
203#endif
204}
205
206struct process {
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        asprintf(&fds, "%s/fd/*", pglob.gl_pathv[i]);
227        glob(fds, GLOB_NOSORT, NULL, &pglob2);
228        free(fds);
229        for(j=0; j<pglob2.gl_pathc; j++)
230        {
231            char path[4096];
232            ssize_t l = readlink(pglob2.gl_pathv[j], path, sizeof(path));
233            if(l <= 0)
234                continue;
235            path[l] = '\0';
236            if(strstr(path, "/dev/pt"))
237            {
238                char * cmdfile;
239                int fd;
240                asprintf(&cmdfile, "%s/cmdline", pglob.gl_pathv[i]);
241                fd = open(cmdfile, O_RDONLY);
242                free(cmdfile);
243                if(fd)
244                {
245                    read(fd, path,  sizeof(path));
246                    (*process_list)[n].cmdline = strdup(path);
247                    close(fd);
248                    n++;
249                    break;
250                }
251            }
252        }
253        globfree(&pglob2);
254    }
255    globfree(&pglob);
256    return n;
257}
258
259long select_process(struct screen_list* screen_list)
260{
261    caca_event_t ev;
262    enum caca_event_type t;
263    int current_line = 1;
264    int refresh = 1;
265    int nb_process, i;
266    int ret = 0;
267    int start = 0;
268    struct process * process_list;
269
270    nb_process = list_process(&process_list);
271
272    screen_list->cv = caca_create_canvas(0, 0);
273    screen_list->dp = caca_create_display(screen_list->cv);
274    if(!screen_list->dp)
275        goto end;
276    caca_set_cursor(screen_list->dp, 0);
277    caca_set_display_title(screen_list->dp, PACKAGE_STRING);
278    while(1)
279    {
280        if(refresh)
281        {
282            caca_set_color_ansi(screen_list->cv, CACA_BLUE, CACA_BLUE);
283            caca_fill_box(screen_list->cv,
284                          0, 0,
285                          caca_get_canvas_width(screen_list->cv),
286                          caca_get_canvas_height(screen_list->cv),
287                          '#');
288            caca_set_color_ansi(screen_list->cv, CACA_DEFAULT, CACA_BLUE);
289            caca_draw_cp437_box(screen_list->cv,
290                                0, 0,
291                                caca_get_canvas_width(screen_list->cv),
292                                caca_get_canvas_height(screen_list->cv)
293                                );
294            caca_printf(screen_list->cv, 2, 2, "Please select a process to grab:");
295            for(i=0; i<nb_process && i<caca_get_canvas_height(screen_list->cv)-4; i++)
296            {
297                if(i == current_line-1)
298                {
299                    caca_set_attr(screen_list->cv, CACA_BOLD);
300                    caca_set_color_ansi(screen_list->cv, CACA_GREEN, CACA_BLUE);
301                    caca_put_char(screen_list->cv, 1, i+3, '>');
302                }
303                else
304                {
305                    caca_set_attr(screen_list->cv, 0);
306                    caca_set_color_ansi(screen_list->cv, CACA_LIGHTGRAY, CACA_BLUE);
307                    caca_put_char(screen_list->cv, 1, i+3, ' ');
308                }
309                caca_printf(screen_list->cv,
310                            3, i+3,
311                            "%5d %s",
312                            process_list[i+start].pid,
313                            process_list[i+start].cmdline);
314            }
315            caca_refresh_display(screen_list->dp);
316            refresh = 0;
317        }
318       
319        if(!caca_get_event(screen_list->dp,
320                           CACA_EVENT_KEY_PRESS
321                           |CACA_EVENT_RESIZE
322                           |CACA_EVENT_QUIT,
323                           &ev, 10000))
324            continue;
325       
326        t = caca_get_event_type(&ev);
327       
328        if(t & CACA_EVENT_KEY_PRESS)
329        {
330            unsigned int c = caca_get_event_key_ch(&ev);
331            switch(c)
332            {
333            case CACA_KEY_UP:
334                if(current_line>1)
335                    current_line--;
336                if(current_line < start && start > 0)
337                    start--;
338                break;
339            case CACA_KEY_DOWN:
340                if(current_line<nb_process)
341                    current_line++;
342                if(current_line > start + caca_get_canvas_height(screen_list->cv)-3)
343                    start++;
344                break;
345            case CACA_KEY_RETURN:
346                ret = process_list[current_line-1].pid;
347                goto end;
348                break;
349            case CACA_KEY_ESCAPE:
350                goto end;
351                break;
352            default:
353                break;
354            }
355            refresh = 1;
356        }
357        else if(t & CACA_EVENT_RESIZE)
358        {
359            refresh = 1;
360        }
361        else if(t & CACA_EVENT_QUIT)
362            goto end;
363    }
364
365end:
366    if(screen_list->dp)
367    {
368        caca_free_display(screen_list->dp);
369        screen_list->dp = NULL;
370    }
371    if(screen_list->cv)
372    {
373        caca_free_canvas(screen_list->cv);
374        screen_list->cv = NULL;
375    }
376    if(nb_process > 0)
377    {
378        for(i=0; i<nb_process; i++)
379        {
380            free(process_list[i].cmdline);
381        }
382        free(process_list);
383    }
384    return ret;
385}
386
Note: See TracBrowser for help on using the repository browser.