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

Last change on this file since 2498 was 2498, checked in by Pascal Terjan, 13 years ago
  • Include config.h in grab.c, else USE_GRAB will never be defined
  • Property svn:eol-style set to native
File size: 6.7 KB
Line 
1/*
2 *  neercs        console-based window manager
3 *  Copyright (c) 2008 Pascal Terjan
4 *                All Rights Reserved
5 *
6 *  $Id$
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#include <errno.h>
18#include <fcntl.h>
19#include <signal.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <unistd.h>
24
25#include "config.h"
26
27#if defined USE_GRAB
28#   include <sys/ioctl.h>
29#   include <sys/ptrace.h>
30#   include <sys/stat.h>
31#   include <sys/syscall.h>
32#   include <sys/user.h>
33#   include <sys/wait.h>
34
35#   include <linux/kdev_t.h>
36#   include <linux/major.h>
37#endif
38
39#include "neercs.h"
40
41#if defined USE_GRAB
42static int memcpy_from_target(pid_t pid, void* dest, void* src, size_t n)
43{
44    unsigned int i;
45    long *d, *s;
46    d = (long*) dest;
47    s = (long*) src;
48    n /= sizeof(long);
49    for (i = 0; i < n; i++) {
50        d[i] = ptrace(PTRACE_PEEKTEXT, pid, s+i, 0);
51        if (errno) {
52            perror("ptrace(PTRACE_PEEKTEXT)");
53            return -1;
54        }
55    }
56    return 0;
57}
58
59static int memcpy_into_target(pid_t pid, void* dest, void* src, size_t n)
60{
61    unsigned int i;
62    long *d, *s;
63    d = (long*) dest;
64    s = (long*) src;
65    for (i = 0; i < n / sizeof(long); i++) {
66        if (ptrace(PTRACE_POKETEXT, pid, d+i, s[i]) == -1) {
67            perror("ptrace(PTRACE_POKETEXT)");
68            return -1;
69        }
70    }
71    return 0;
72}
73
74static int do_syscall(pid_t pid, struct user_regs_struct *regs)
75{
76
77    struct user_regs_struct oregs;
78    long oinst;
79
80    if(ptrace(PTRACE_GETREGS, pid, NULL, &oregs) < 0)
81    {
82        return errno;
83    }
84
85    regs->esp = oregs.esp-4;
86
87    oinst = ptrace(PTRACE_PEEKTEXT, pid, regs->esp, 0);
88    if (errno) {
89        fprintf(stderr, "PTRACE_PEEKTEXT failed\n");
90        return errno;
91    }
92
93    if (ptrace(PTRACE_POKETEXT, pid, regs->esp, 0x80cd) < 0) { /* int 0x80 */
94        fprintf(stderr, "PTRACE_POKETEXT failed\n");
95        return errno;
96    }
97
98    regs->eip = regs->esp;
99    if(ptrace(PTRACE_SETREGS, pid, NULL, regs) < 0)
100    {
101        return errno;
102    }
103
104    if(ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL) < 0)
105    {
106        return errno;
107    }
108    waitpid(pid, NULL, 0);
109
110    if(ptrace(PTRACE_GETREGS, pid, NULL, regs) < 0)
111    {
112        return errno;
113    }
114
115    if(ptrace(PTRACE_SETREGS, pid, NULL, &oregs) < 0)
116    {
117        fprintf(stderr, "PTRACE_SETREGS failed\n");
118        return errno;
119    }
120
121    if(ptrace(PTRACE_POKETEXT, pid, oregs.esp-4 , oinst) < 0)
122    {
123        fprintf(stderr, "PTRACE_POKETEXT failed\n");
124        return errno;
125    }
126
127    return 0;
128}
129
130static int do_close(pid_t pid, int fd)
131{
132    struct user_regs_struct regs;
133    if(ptrace(PTRACE_GETREGS, pid, NULL, &regs) < 0)
134    {
135        fprintf(stderr, "PTRACE_GETREGS failed\n");
136        return errno;
137    }
138
139    regs.eax = SYS_close;
140    regs.ebx = fd;
141
142    return do_syscall(pid, &regs);
143}
144
145static int do_dup2(pid_t pid, int oldfd, int newfd)
146{
147    struct user_regs_struct regs;
148    if(ptrace(PTRACE_GETREGS, pid, NULL, &regs) < 0)
149    {
150        fprintf(stderr, "PTRACE_GETREGS failed\n");
151        return errno;
152    }
153
154    regs.eax = SYS_dup2;
155    regs.ebx = oldfd;
156    regs.ecx = newfd;
157
158    return do_syscall(pid, &regs);
159}
160
161static int do_open(pid_t pid, char *path, int mode, int *fd)
162{
163    struct user_regs_struct regs;
164    void *backup_page;
165    void *target_page = (void*)(0x08048000);
166    size_t size = (1<<12); /* 4K */
167    int ret;
168
169    /* Backup the page that we will use */
170    backup_page = malloc(size);
171    if (memcpy_from_target(pid, backup_page, target_page, size) < 0)
172        return -1;
173
174    if(ptrace(PTRACE_GETREGS, pid, NULL, &regs) < 0)
175    {
176        fprintf(stderr, "PTRACE_GETREGS failed\n");
177        return errno;
178    }
179
180    /* +4 because it's trcuncated on a multiple of 4 and we need 1 */
181    memcpy_into_target(pid, target_page, path, strlen(path)+4);
182
183    regs.eax = SYS_open;
184    regs.ebx = (long)target_page;
185    regs.ecx = O_RDWR;
186    regs.edx = 0755;
187
188    if((ret = do_syscall(pid, &regs)) != 0)
189    {
190        return ret;
191    }
192
193    /* Restore the pages */
194    memcpy_into_target(pid, target_page, backup_page, size);
195
196    *fd  = regs.eax;
197
198    return 0;
199}
200
201static int do_setsid(pid_t pid) {
202    struct user_regs_struct regs;
203    int ret;
204
205    if(ptrace(PTRACE_GETREGS, pid, NULL, &regs) < 0)
206    {
207        fprintf(stderr, "PTRACE_GETREGS failed\n");
208        return errno;
209    }
210
211    debug("Running setsid on process %d (sid=%d)", pid, getsid(pid));
212
213    regs.eax = SYS_setpgid;
214    regs.ebx = 0;
215    regs.ecx = getsid(pid);
216
217    if((ret = do_syscall(pid, &regs)) != 0)
218    {
219        return ret;
220    }
221
222    if (regs.eax != 0) {
223        fprintf(stderr, "setpgid failed\n");
224        return -regs.eax;
225    }
226
227    regs.eax = SYS_setsid;
228
229    if((ret = do_syscall(pid, &regs)) != 0)
230    {
231        return ret;
232    }
233
234    debug("pid %d has now sid %d", pid, getsid(pid));
235
236    if (regs.eax == -1) {
237        fprintf(stderr, "getsid failed\n");
238        return -regs.eax;
239    }
240
241    return 0;
242}
243#endif
244
245int grab_process(pid_t pid, char *ptyname, int ptyfd)
246{
247#if defined USE_GRAB
248    int i, fd = 0, mode, ret;
249    char fdstr[1024];
250    char to_open[3];
251    struct stat stat_buf;
252
253    debug("pty is %s", ptyname);
254
255    kill(pid, SIGSTOP);
256    if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0)
257    {
258        fprintf(stderr, "Cannot access process %d\n", pid);
259        return errno;
260    }
261    kill(pid, SIGCONT);
262    waitpid(pid, NULL, 0);
263
264    for(i=0; i<=2; i++)
265    {
266        snprintf(fdstr, sizeof(fdstr), "/proc/%d/fd/%d", pid, i);
267        to_open[i]=0;
268        lstat(fdstr, &stat_buf);
269        if ((stat_buf.st_mode & S_IRUSR) && (stat_buf.st_mode & S_IWUSR))
270            mode = O_RDWR;
271        else if (stat_buf.st_mode & S_IWUSR)
272            mode = O_WRONLY;
273        else
274            mode = O_RDONLY;
275
276        if (stat(fdstr, &stat_buf) < 0)
277            continue;
278
279        if (!S_ISCHR(stat_buf.st_mode) || MAJOR(stat_buf.st_rdev) != UNIX98_PTY_SLAVE_MAJOR)
280            continue;
281
282        debug("fd %d is a pty", i);
283        if((ret = do_close(pid, i)))
284        {
285            fprintf(stderr, "do_close %s\n", strerror(ret));
286        }
287        to_open[i]=1;
288    }
289
290    if((ret = do_setsid(pid)))
291    {
292        fprintf(stderr, "do_setsid %s\n", strerror(ret));
293    }
294
295    for(i=0; i<=2; i++)
296    {
297        if(!to_open[i])
298            continue;
299        if((ret = do_open(pid, ptyname, mode, &fd)))
300        {
301            fprintf(stderr, "do_open %s\n", strerror(ret));
302        }
303        if((ret = do_dup2(pid, fd, i)))
304        {
305            fprintf(stderr, "do_dup2 %s\n", strerror(ret));
306        }
307    }
308
309    kill(pid, SIGWINCH);
310    ptrace(PTRACE_DETACH, pid, 0, 0);
311
312    return 0;
313#else
314    return -1;
315#endif
316}
317
Note: See TracBrowser for help on using the repository browser.