Make it easier to add rsync arguments.
[util-vserver.git] / src / vps.c
1 // $Id$    --*- c -*--
2
3 // Copyright (C) 2004 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 //  
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; version 2 of the License.
8 //  
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //  
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18
19 #ifdef HAVE_CONFIG_H
20 #  include <config.h>
21 #endif
22
23 #include "util.h"
24 #include "pathconfig.h"
25
26 #include <lib/vserver.h>
27 #include <lib/fmt.h>
28 #include <assert.h>
29 #include <fcntl.h>
30 #include <signal.h>
31 #include <errno.h>
32
33 #define ENSC_WRAPPERS_VSERVER   1
34 #define ENSC_WRAPPERS_STDLIB    1
35 #define ENSC_WRAPPERS_UNISTD    1
36 #define ENSC_WRAPPERS_FCNTL     1
37 #include <wrappers.h>
38
39 #define CTXNR_WIDTH     5
40 #define HUNK_SIZE       0x4000
41 #define CONTEXT_WIDTH   20
42 #define CONTEXT_PLACE   "                    "
43
44 int wrapper_exit_code = 254;
45
46 struct ContextMapping {
47     xid_t               ctx;
48     char const *        id;
49 };
50
51 static struct ContextMapping            *mapping    = 0;
52 static size_t                           mapping_len = 0;
53
54
55 static void
56 showHelp(int fd, char const *cmd, int res)
57 {
58   WRITE_MSG(fd, "Usage:  ");
59   WRITE_STR(fd, cmd);
60   WRITE_MSG(fd,
61             " <ps-opts>*\n\n"
62             "Please report bugs to " PACKAGE_BUGREPORT "\n");
63   exit(res);
64 }
65
66 static void
67 showVersion()
68 {
69   WRITE_MSG(1,
70             "vps " VERSION " -- shows processes in vserver-contexts\n"
71             "This program is part of " PACKAGE_STRING "\n\n"
72             "Copyright (C) 2004 Enrico Scholz\n"
73             VERSION_COPYRIGHT_DISCLAIMER);
74   exit(0);
75 }
76
77
78 static size_t
79 writeContextInfo(xid_t ctx, char const *name)
80 {
81   size_t        l1  = name==0 ? 0 : strlen(name);
82   size_t        res = CTXNR_WIDTH + 1;
83   
84   if (ctx==VC_NOCTX) {
85     if (3<CTXNR_WIDTH) Vwrite(1, CONTEXT_PLACE, CTXNR_WIDTH-3);
86     Vwrite(1, "ERR ", 4);
87   }
88   else {
89     char        buf[sizeof(ctx)*3+1];
90     size_t      l = utilvserver_fmt_ulong(buf, ctx);
91
92     if (l<CTXNR_WIDTH) Vwrite(1, CONTEXT_PLACE, CTXNR_WIDTH-l);
93     Vwrite(1, buf, l);
94     Vwrite(1, " ", 1);
95   }
96
97   if (l1!=0) {
98     assert(name!=0);
99     Vwrite(1, name, l1);
100   }
101
102   return res+l1;
103 }
104
105 static xid_t
106 extractCtx(char *pid_str)
107 {
108   pid_t         pid;
109   
110   while (*pid_str==' ') ++pid_str;
111   pid = atoi(pid_str);
112
113   return vc_get_task_xid(pid);
114 }
115
116 static char const *
117 resolveCtx(xid_t ctx)
118 {
119   char const *  res;
120   size_t        i;
121
122   for (i=0; i<mapping_len; ++i)
123     if (mapping[i].ctx==ctx) return mapping[i].id;
124
125   ++mapping_len;
126   mapping = Erealloc(mapping, mapping_len * sizeof(mapping[0]));
127   
128   if (ctx==0)      res = strdup("MAIN");
129   else if (ctx==1) res = strdup("ALL_PROC");
130   else {
131     vcCfgStyle  style = vcCFG_AUTO;
132     char        *tmp  = vc_getVserverByCtx(ctx, &style,0);
133     if (tmp) res = vc_getVserverName(tmp, style);
134     else     res = 0;
135     free(tmp);
136   }
137
138   mapping[mapping_len-1].ctx = ctx;
139   mapping[mapping_len-1].id  = res;
140   return res;
141 }
142
143 static char *
144 readOutput(int fd, size_t *total_len)
145 {
146   size_t        len  = 2*HUNK_SIZE;
147   char          *buf = Emalloc(len+1);
148   size_t        offset = 0;
149
150   for (;;) {
151     size_t      l;
152
153     while (offset >= len) {
154       len += HUNK_SIZE;
155       buf  = Erealloc(buf, len+1);
156     }
157     
158     l = Eread(fd, buf+offset, len - offset);
159     if (l==0) break;
160
161     offset += l;
162   }
163
164   buf[offset] = '\0';
165
166   if (total_len)
167     *total_len = offset;
168
169   return buf;
170 }
171
172 static void
173 processOutput(char *data, size_t len)
174 {
175   size_t        pid_end;
176   char *        eol_pos = strchr(data, '\n');
177   char *        pos;
178
179   if (eol_pos==0) eol_pos  = data + len;
180   else            *eol_pos = '\0';
181   
182   pos = strstr(data, "PID");
183   if (pos==0) {
184     WRITE_MSG(2, "Failed to parse ps-output\n");
185     exit(wrapper_exit_code);
186   }
187
188   pid_end = pos-data + 4;
189
190   Vwrite(1, data, pid_end);
191   Vwrite(1, "CONTEXT" CONTEXT_PLACE, CONTEXT_WIDTH);
192   Vwrite(1, data+pid_end, eol_pos-(data+pid_end));
193   Vwrite(1, "\n", 1);
194
195   len -= eol_pos-data;
196   data = eol_pos+1;
197   
198   while (len > 1) {
199     char const  *vserver_name = 0;
200     xid_t       ctx;
201     size_t      l;
202
203     --len;
204     eol_pos = strchr(data, '\n');
205     
206     if (eol_pos==0) eol_pos  = data + len;
207
208     ctx          = extractCtx(data + pid_end - 6);
209     vserver_name = resolveCtx(ctx);
210
211     Vwrite(1, data, pid_end);
212     l = writeContextInfo(ctx, vserver_name);
213     if (l<CONTEXT_WIDTH) Vwrite(1, CONTEXT_PLACE, CONTEXT_WIDTH-l);
214     else                 Vwrite(1, " ", 1);
215     Vwrite(1, data+pid_end, eol_pos-(data+pid_end));
216     Vwrite(1, "\n", 1);
217     
218     len -= eol_pos-data;
219     data = eol_pos+1;
220   }
221 }
222
223 int main(int argc, char *argv[])
224 {
225   int           p[2];
226   pid_t         pid;
227   char *        data;
228   size_t        len;
229   char const *  errptr;
230
231   if (argc>1) {
232     if (strcmp(argv[1], "--help")   ==0) showHelp(1, argv[0], 0);
233     if (strcmp(argv[1], "--version")==0) showVersion();
234   }
235
236   signal(SIGCHLD, SIG_DFL);
237
238   if (!switchToWatchXid(&errptr)) {
239     perror(errptr);
240     exit(wrapper_exit_code);
241   }
242
243   if (access("/proc/uptime",R_OK)==-1 && errno==ENOENT)
244     WRITE_MSG(2,
245               "WARNING: can not access /proc/uptime. Usually, this is caused by\n"
246               "         procfs-security. Please read the FAQ for more details\n"
247               "         http://linux-vserver.org/Proc-Security\n");
248
249   Epipe(p);
250   pid = Efork();
251
252   if (pid==0) {
253     int         fd = Eopen("/dev/null", O_RDONLY, 0);
254     Edup2(fd,   0);
255     Edup2(p[1], 1);
256     Eclose(p[0]);
257     Eclose(p[1]);
258     Eclose(fd);
259
260     argv[0] = "ps";
261
262     Eexecv(PS_PROG, argv);
263   }
264
265   Eclose(p[1]);
266   data = readOutput(p[0], &len);
267   Eclose(p[0]);
268   
269   processOutput(data, len);
270   vc_exitLikeProcess(pid, wrapper_exit_code);
271 }