Disable SSH_AUTH_SOCK forwarding by default
[screenenv.git] / sessions.c
blob092d1961771769e00475c6c7ff1de73e354af42a
1 /* Examine all the clients attached to our screen and find the one
2 * with the least idle time. */
4 /* Inconsistent results may appear in case of nested screens on the
5 * same system. */
7 #define _GNU_SOURCE
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <ctype.h>
14 #include <fcntl.h>
15 #include <unistd.h>
16 #include <dirent.h>
17 #include <alloca.h>
19 #include "screenenv.h"
21 struct procinfo {
22 pid_t pid, ppid;
23 char *name;
24 char buf_[1024];
27 /* Load procinfo for given process. */
28 static void get_procinfo(pid_t pid, struct procinfo *pi)
30 pi->pid = pid;
31 pi->name = NULL;
33 char statfname[64];
34 snprintf(statfname, sizeof(statfname), "/proc/%d/stat", pid);
35 int fd = open(statfname, O_RDONLY);
36 if (fd < 0) return;
38 ssize_t r = read(fd, pi->buf_, sizeof(pi->buf_) - 1);
39 if (r <= 0) return;
40 pi->buf_[r] = 0;
41 close(fd);
43 pi->name = strchr(pi->buf_, '(') + 1;
44 char *nameend = strrchr(pi->buf_, ')');
45 *nameend = 0;
46 pi->ppid = atol(nameend + 4);
49 /* Find master screen process. */
50 static pid_t get_screen_pid(pid_t base_pid)
52 struct procinfo pi = { .ppid = base_pid };
53 do {
54 get_procinfo(pi.ppid, &pi);
55 } while (pi.name && strcmp(pi.name, "screen") && pi.ppid > 0);
56 if (!pi.name || pi.ppid <= 0)
57 return -1;
58 return pi.pid;
62 /* Find pts of a process with the least idle time. */
63 static dev_t get_active_pts(pid_t pid)
65 char fddname[64];
66 snprintf(fddname, sizeof(fddname), "/proc/%d/fd", pid);
67 DIR *fdd = opendir(fddname);
68 if (!fdd) return 0;
70 dev_t bestpts = 0;
71 time_t bestatime = 0;
73 struct dirent *de, *res;
74 de = alloca(sizeof(*de) + 4096);
75 while (1) {
76 if (readdir_r(fdd, de, &res) != 0)
77 return 0;
78 if (!res)
79 break;
81 /* Pick files we can see and are pts devices */
82 struct stat s;
83 if (fstatat(dirfd(fdd), de->d_name, &s, 0) != 0)
84 continue;
85 if (major(s.st_rdev) != 136)
86 continue;
87 /* and choose the one with the least idletime. */
88 if (s.st_atime < bestatime)
89 continue;
90 bestpts = s.st_rdev;
91 bestatime = s.st_atime;
94 closedir(fdd);
95 return bestatime > 0 ? bestpts : 0;
99 /* Scan all processes for a screen with the given pts as stdin. */
100 static pid_t get_client_by_pts(dev_t pts)
102 DIR *procd = opendir("/proc");
103 if (!procd) return 0;
105 struct dirent *de, *res;
106 de = alloca(sizeof(*de) + 4096);
107 while (1) {
108 if (readdir_r(procd, de, &res) != 0)
109 return 0;
110 if (!res)
111 break;
112 int namelen = strlen(de->d_name);
114 /* Pick numerical directories */
115 if (de->d_type != DT_DIR || !isdigit(de->d_name[0]))
116 continue;
118 /* Peek at fd 0 */
119 char name[namelen + 7];
120 stpcpy(stpcpy(name, de->d_name), "/fd/0");
122 struct stat s;
123 if (fstatat(dirfd(procd), name, &s, 0) != 0)
124 continue;
125 if (s.st_rdev != pts)
126 continue;
128 /* Is this screen? */
129 pid_t pid = atol(de->d_name);
130 struct procinfo pi;
131 get_procinfo(pid, &pi);
132 if (pi.name == NULL || strcmp(pi.name, "screen"))
133 continue;
135 closedir(procd);
136 return pid;
139 closedir(procd);
140 return -1;
144 /* Find the client screen process watching base_pid with the least
145 * idle time. */
146 pid_t get_client_pid(pid_t base_pid)
148 static pid_t screen_pid;
149 if (screen_pid < 0) return -1;
150 if (screen_pid == 0) {
151 screen_pid = get_screen_pid(base_pid);
152 if (screen_pid < 0) return -1;
154 dev_t client_pts = get_active_pts(screen_pid);
155 if (client_pts == 0) return -1;
156 return get_client_by_pts(client_pts);
160 #if 0
161 int main(void)
163 printf("%d\n", get_client_pid(getpid()));
165 #endif