use $(LIBENSCVECTOR) instead of libensc_vector.a
[util-vserver.git] / util-vserver / src / vserver-info.c
1 // $Id$    --*- c -*--
2
3 // Copyright (C) 2003 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 "lib/utils-legacy.h"
24 #include "pathconfig.h"
25 #include "util.h"
26
27 #include "internal.h"
28 #include "vserver.h"
29
30 #include <stdlib.h>
31 #include <getopt.h>
32 #include <assert.h>
33 #include <stdbool.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 #include <sys/utsname.h>
37 #include <dirent.h>
38 #include <strings.h>
39
40 #define ENSC_WRAPPERS_FCNTL     1
41 #define ENSC_WRAPPERS_IO        1
42 #define ENSC_WRAPPERS_UNISTD    1
43 #define ENSC_WRAPPERS_VSERVER   1
44 #include <wrappers.h>
45
46 #undef _POSIX_SOURCE
47 #include "capability-compat.h"
48
49 typedef enum { tgNONE,tgCONTEXT, tgID, tgRUNNING,
50                tgVDIR, tgNAME, tgCFGDIR, tgAPPDIR,
51                tgAPIVER, tgPXID,
52                tgINITPID, tgINITPID_PID,
53                tgXID, tgUTS, tgSYSINFO,
54                tgFEATURE, tgCANONIFY,
55                tgVERIFYCAP, tgXIDTYPE,
56 }       VserverTag;
57
58 static struct {
59     char const * const  tag;
60     VserverTag const    val;
61     char const * const  descr;
62 }  const TAGS[] = {
63   { "CONTEXT", tgCONTEXT, ("the current and/or assigned context; when an optinal argument "
64                            "evaluates to false,only the current context will be printed") },
65   { "ID",      tgID,      "gives out the vserver-id for the context-xid" },
66   { "RUNNING", tgRUNNING, "gives out '1' when vserver is running; else, it fails without output" },
67   { "VDIR",    tgVDIR,    "gives out the root-directory of the vserver" },
68   { "NAME",    tgNAME,    "gives out the name of the vserver" },
69   { "CFGDIR",  tgCFGDIR,  "gives out the configuration directory of the vserver" },
70   { "APPDIR",  tgAPPDIR,  "gives out the name of the toplevel application cfgdir" },
71   { "INITPID",     tgINITPID,     "gives out the initpid of the given context" },
72   { "INITPID_PID", tgINITPID_PID, "gives out the initpid of the given pid" },
73   { "XID",         tgXID,         "gives out the context-id of the given pid" },
74   { "APIVER",      tgAPIVER,      "gives out the version of the kernel API" },
75   { "UTS",         tgUTS,         ("gives out an uts-entry; possible entries are "
76                                    "context, sysname, nodename, release, version, "
77                                    "machine and domainname") },
78   { "SYSINFO",     tgSYSINFO,     "gives out information about the systen" },
79   { "FEATURE",     tgFEATURE,     "returns 0 iff the queried feature is supported" },
80   { "PXID",        tgPXID,        "returns the xid of the parent context" },
81   { "CANONIFY",    tgCANONIFY,    "canonifies the vserver-name and removes dangerous characters" },
82   { "VERIFYCAP",   tgVERIFYCAP,   "test if the kernel supports linux capabilities" },
83   { "XIDTYPE",     tgXIDTYPE,     "returns the type of the given XID" },
84 };
85
86 int wrapper_exit_code = 1;
87
88 static struct option const
89 CMDLINE_OPTIONS[] = {
90   { "help",     no_argument,  0, 'h' },
91   { "version",  no_argument,  0, 'v' },
92   { 0,0,0,0 }
93 };
94
95
96 static void
97 showHelp(int fd, char const *cmd, int res)
98 {
99   WRITE_MSG(fd, "Usage:  ");
100   WRITE_STR(fd, cmd);
101   WRITE_MSG(fd,
102             " [-ql] <vserver>|<pid>|<context> <tag>\n"
103             "Please report bugs to " PACKAGE_BUGREPORT "\n");
104   exit(res);
105 }
106
107 static void
108 showVersion()
109 {
110   WRITE_MSG(1,
111             "vserver-info " VERSION " -- returns information about vservers\n"
112             "This program is part of " PACKAGE_STRING "\n\n"
113             "Copyright (C) 2003 Enrico Scholz\n"
114             VERSION_COPYRIGHT_DISCLAIMER);
115   exit(0);
116 }
117
118 static void
119 showTags()
120 {
121   char const *          delim = "";
122   size_t        i;
123
124   WRITE_MSG(1, "Valid tags are: ");
125   for (i=0; i<DIM_OF(TAGS); ++i) {
126     WRITE_STR(1, delim);
127     WRITE_STR(1, TAGS[i].tag);
128
129     delim = ", ";
130   }
131   WRITE_MSG(1, "\n");
132   exit(0);
133 }
134
135 static VserverTag
136 stringToTag(char const *str)
137 {
138   size_t        i;
139   for (i=0; i<DIM_OF(TAGS); ++i)
140     if (strcmp(TAGS[i].tag, str)==0) return TAGS[i].val;
141
142   return tgNONE;
143 }
144
145 static vc_uts_type
146 utsText2Tag(char const *str)
147 {
148   if      (strcmp(str, "context")   ==0) return vcVHI_CONTEXT;
149   else if (strcmp(str, "sysname")   ==0) return vcVHI_SYSNAME;
150   else if (strcmp(str, "nodename")  ==0) return vcVHI_NODENAME;
151   else if (strcmp(str, "release")   ==0) return vcVHI_RELEASE;
152   else if (strcmp(str, "version")   ==0) return vcVHI_VERSION;
153   else if (strcmp(str, "machine")   ==0) return vcVHI_MACHINE;
154   else if (strcmp(str, "domainname")==0) return vcVHI_DOMAINNAME;
155   else {
156     WRITE_MSG(2, "Unknown UTS tag\n");
157     exit(1);
158   }
159 }
160
161 static bool
162 verifyCap()
163 {
164   struct __user_cap_header_struct header;
165   struct __user_cap_data_struct user;
166   header.version = _LINUX_CAPABILITY_VERSION;
167   header.pid     = 0;
168
169   if (getuid()!=0) {
170     WRITE_MSG(2, "'VERIFYCAP' can be executed as root only\n");
171     return false;
172   }
173
174 //  if( prctl( PR_SET_KEEPCAPS, 1,0,0,0 ) < 0 ) {
175 //    perror( "prctl:" );
176 //    return false;
177 //  }
178   
179   if (capget(&header, &user)==-1) {
180     perror("capget()");
181     return false;
182   }
183
184   user.effective   = 0;
185   user.permitted   = 0;
186   user.inheritable = 0;
187
188   if (capset(&header, &user)==-1) {
189     perror("capset()");
190     return false;
191   }
192
193   return chroot("/")==-1;
194 }
195
196 static char *
197 getAPIVer(char *buf)
198 {
199   int           v = vc_get_version();
200   size_t        l;
201   
202   if (v==-1) return 0;
203
204   
205   l = utilvserver_fmt_xulong(0, (unsigned int)v);
206   memcpy(buf, "0x00000000", 10);
207   utilvserver_fmt_xulong(buf+2+8-l, (unsigned int)v);
208
209   return buf;
210 }
211
212 static char *
213 getXid(char *buf, char const *pid_str)
214 {
215   pid_t         pid = atoi(pid_str);
216   xid_t         xid = vc_get_task_xid(pid);
217
218   if (xid==VC_NOCTX) perror("vc_get_task_xid()");
219   else {
220     utilvserver_fmt_long(buf, xid);
221     return buf;
222   }
223
224   return 0;
225 }
226
227 static char *
228 getPXid(char UNUSED *buf, char const UNUSED *vserver)
229 {
230   // TODO: implement me when available
231   return 0;
232 }
233
234 static char *
235 getInitPid_native(char *buf, xid_t xid)
236 {
237   struct vc_vx_info             info;
238   
239   if (vc_get_vx_info(xid, &info)==-1) perror("vc_get_vx_info()");
240   else {
241     utilvserver_fmt_long(buf, info.initpid);
242     return buf;
243   }
244
245   return 0;
246 }
247
248 #if defined(VC_ENABLE_API_COMPAT) || defined(VC_ENABLE_API_V11)
249 static int
250 selectPid(struct dirent const *ent)
251 {
252   return atoi(ent->d_name)!=0;
253 }
254
255 static bool
256 getInitPid_internal(pid_t pid, xid_t xid, pid_t *res)
257 {
258   *res = -1;
259   
260   for (;*res==-1;) {
261     size_t                      bufsize = utilvserver_getProcEntryBufsize();
262     char                        buf[bufsize+1];
263     char                        *pos = 0;
264
265     pos = utilvserver_getProcEntry(pid, "\ns_context: ", buf, bufsize);
266     if (pos==0 && errno==EAGAIN) continue;
267
268     if (pos==0 || (xid_t)atoi(pos)!=xid) return false;
269
270     buf[bufsize] = '\0';
271     pos          = strstr(buf, "\ninitpid: ");
272     
273     if (pos!=0) {
274       pos       += sizeof("\ninitpid: ")-1;
275       if (strncmp(pos, "none", 4)==0) *res = -1;
276       else                            *res = atoi(pos);
277     }
278   }
279
280   return true;
281 }
282
283 static char *
284 getInitPid_emulated(char *buf, xid_t xid)
285 {
286   struct dirent **namelist;
287   int           n;
288
289   switchToWatchXid(0);  // ignore errors silently...
290   n = scandir("/proc", &namelist, selectPid, alphasort);
291   if (n<0) perror("scandir()");
292   else while (n--) {
293     pid_t       pid;
294     if (!getInitPid_internal(atoi(namelist[n]->d_name), xid, &pid)) continue;
295
296     utilvserver_fmt_long(buf, pid);
297     return buf;
298   }
299
300   return 0;
301 }
302 #endif // VC_ENABLE_API_COMPAT
303
304 static char *
305 getInitPid(char *buf, xid_t xid)
306 {
307   if (vc_isSupported(vcFEATURE_VINFO))
308     return getInitPid_native(buf, xid);
309   else
310     return getInitPid_emulated(buf, xid);
311 }
312
313 static char *
314 getInitPidPid(char *buf, char const *vserver)
315 {
316   struct vc_vx_info     info;
317   pid_t                 pid = atoi(vserver);
318   xid_t                 xid = vc_get_task_xid(pid);
319
320   if (xid==VC_NOCTX) perror("vc_get_task_xid()");
321   else if (vc_get_vx_info(xid, &info)==-1) perror("vc_get_vx_info()");
322   else {
323     utilvserver_fmt_long(buf, info.initpid);
324     return buf;
325   }
326
327   return 0;
328 }
329
330 static char *
331 getUTS(char *buf, xid_t xid, size_t argc, char * argv[])
332 {
333   if (argc>0) {
334     vc_uts_type type = utsText2Tag(argv[0]);
335     if (vc_get_vhi_name(xid, type, buf, sizeof(buf)-1)==-1)
336       perror("vc_get_vhi_name()");
337     else
338       return buf;
339   }
340   else {
341     bool                is_passed = false;
342     char                tmp[128];
343 #define APPEND_UTS(TYPE)                                                \
344     (((vc_get_vhi_name(xid, TYPE, tmp, sizeof(tmp)-1)!=-1) && (strcat(buf, tmp), strcat(buf, " "), is_passed=true)) || \
345      (strcat(buf, "??? ")))
346
347     if (APPEND_UTS(vcVHI_CONTEXT) &&
348         APPEND_UTS(vcVHI_SYSNAME) &&
349         APPEND_UTS(vcVHI_NODENAME) &&
350         APPEND_UTS(vcVHI_RELEASE) &&
351         APPEND_UTS(vcVHI_VERSION) &&
352         APPEND_UTS(vcVHI_MACHINE) &&
353         APPEND_UTS(vcVHI_DOMAINNAME) &&
354         is_passed)
355       return buf;
356
357     perror("vc_get_vhi_name()");
358 #undef APPEND_UTS
359   }
360
361   return 0;
362 }
363
364 static int
365 printSysInfo(char *buf)
366 {
367   int                   fd = open(PKGLIBDIR "/FEATURES.txt", O_RDONLY);
368   struct utsname        uts;
369
370   if (uname(&uts)==-1)
371     perror("uname()");
372   else {
373     WRITE_MSG(1,
374               "Versions:\n"
375               "                   Kernel: ");
376     WRITE_STR(1, uts.release);
377     WRITE_MSG(1, "\n"
378               "                   VS-API: ");
379
380     memset(buf, 0, 128);
381     if (getAPIVer(buf)) WRITE_STR(1, buf);
382     else                WRITE_MSG(1, "???");
383     
384     WRITE_MSG(1, "\n"
385               "             util-vserver: " PACKAGE_VERSION "; " __DATE__ ", " __TIME__"\n"
386               "\n");
387   }
388
389   if (fd==-1)
390     WRITE_MSG(1, "FEATURES.txt not found\n");
391   else {
392     off_t               l  = Elseek(fd, 0, SEEK_END);
393     Elseek(fd, 0, SEEK_SET);
394     {
395       char              buf[l];
396       EreadAll(fd, buf, l);
397       EwriteAll(1, buf, l);
398     }
399     Eclose(fd);
400   }
401
402   return EXIT_SUCCESS;
403 }
404
405 static char *
406 getContext(char *buf, char const *vserver, bool allow_only_static)
407 {
408   xid_t         xid = vc_getVserverCtx(vserver, vcCFG_AUTO,
409                                        allow_only_static, 0);
410   if (xid==VC_NOCTX) return 0;
411   
412   utilvserver_fmt_long(buf, xid);
413   return buf;
414 }
415
416 static char const *
417 getXIDType(xid_t xid, int argc, char *argv[])
418 {
419   char const *          tp;
420   
421   switch (vc_getXIDType(xid)) {
422     case vcTYPE_INVALID         :  tp = "invalid"; break;
423     case vcTYPE_MAIN            :  tp = "main";    break;
424     case vcTYPE_WATCH           :  tp = "watch";   break;
425     case vcTYPE_STATIC          :  tp = "static";  break;
426     case vcTYPE_DYNAMIC         :  tp = "dynamic"; break;
427     default                     :  tp = 0;         break;
428   }
429
430   if (argc==0 || tp==0)
431     return tp;
432
433   while (argc>0) {
434     --argc;
435     if (strcasecmp(argv[argc], tp)==0) return tp;
436   }
437
438   return 0;
439 }
440
441 static int
442 testFeature(int argc, char *argv[])
443 {
444   return (argc>0 && vc_isSupportedString(argv[0])) ? EXIT_SUCCESS : EXIT_FAILURE;
445 }
446
447 static bool
448 str2bool(char const *str)
449 {
450   return atoi(str)!=0 || strchr("yYtY", str[0])!=0 || strcasecmp("true", str)==0;
451 }
452
453 static int
454 execQuery(char const *vserver, VserverTag tag, int argc, char *argv[])
455 {
456   char const *          res = 0;
457   char                  buf[sizeof(xid_t)*4 + 1024 + strlen(vserver)];
458
459   memset(buf, 0, sizeof buf);
460   switch (tag) {
461     case tgNAME         :  res = vc_getVserverName(vserver, vcCFG_AUTO); break;
462     case tgVDIR         :
463       res = vc_getVserverVdir(vserver, vcCFG_AUTO, argc>0 && atoi(argv[0]));
464       break;
465     case tgCFGDIR       :  res = vc_getVserverCfgDir(vserver, vcCFG_AUTO);     break;
466     case tgAPPDIR       :
467       res = vc_getVserverAppDir(vserver, vcCFG_AUTO, argc==0 ? "" : argv[0]);
468       break;
469       
470     case tgRUNNING      :
471       res = (vc_getVserverCtx(vserver, vcCFG_AUTO, false, 0)==VC_NOCTX) ? 0 : "1";
472       break;
473
474     case tgCANONIFY     :
475       strcpy(buf, vserver);
476       if (canonifyVserverName(buf)>0) res = buf;
477       break;
478       
479     case tgCONTEXT      :  res = getContext(buf, vserver,
480                                             argc==0 || str2bool(argv[0])); break;
481     case tgINITPID_PID  :  res = getInitPidPid(buf, vserver);  break;
482     case tgAPIVER       :  res = getAPIVer(buf);               break;
483     case tgXID          :  res = getXid(buf, vserver);         break;
484     case tgPXID         :  res = getPXid(buf, vserver);        break;
485     case tgSYSINFO      :  return printSysInfo(buf);           break;
486     case tgFEATURE      :  return testFeature(argc,argv);      break;
487     case tgVERIFYCAP    :  return verifyCap() ? 0 : 1;         break;
488
489
490     default             : {
491       xid_t             xid = *vserver!='\0' ? vc_xidopt2xid(vserver,true,0) : VC_SAMECTX;
492
493       switch (tag) {
494         case tgID       :  res = vc_getVserverByCtx(xid,0,0);  break;
495         case tgINITPID  :  res = getInitPid(buf, xid);         break;
496         case tgUTS      :  res = getUTS(buf, xid, argc, argv); break;
497         case tgXIDTYPE  :  res = getXIDType(xid, argc, argv);  break;
498     
499         default         :  assert(false); abort();  // TODO
500       }
501     }
502   }
503
504   if (res==0) return EXIT_FAILURE;
505   WRITE_STR(1, res);
506   WRITE_MSG(1, "\n");
507   return EXIT_SUCCESS;
508 }
509
510 int main(int argc, char *argv[])
511 {
512   bool          quiet = false;
513   char const *  vserver;
514   VserverTag    tag;
515   
516   while (1) {
517     int         c = getopt_long(argc, argv, "ql", CMDLINE_OPTIONS, 0);
518     if (c==-1) break;
519
520     switch (c) {
521       case 'h'          :  showHelp(1, argv[0], 0);
522       case 'v'          :  showVersion();
523       case 'l'          :  showTags();
524       case 'q'          :  quiet = true; break;
525       default           :
526         WRITE_MSG(2, "Try '");
527         WRITE_STR(2, argv[0]);
528         WRITE_MSG(2, " --help\" for more information.\n");
529         exit(1);
530         break;
531     }
532   }
533
534   if (optind+2>argc) {
535     execQuery("-", tgSYSINFO, 0, 0);
536     WRITE_MSG(2, "\nAssumed 'SYSINFO' as no other option given; try '--help' for more information.\n");
537     exit(0);
538   }
539
540   vserver = argv[optind];
541   tag     = stringToTag(argv[optind+1]);
542
543   if (tag==tgNONE) {
544     WRITE_MSG(2, "Unknown tag; use '-l' to get list of valid tags\n");
545     exit(1);
546   }
547
548   if (quiet) {
549     int         fd = Eopen("/dev/null", O_WRONLY, 0644);
550     Edup2(fd, 1);
551     Eclose(fd);
552   }
553
554   return execQuery(vserver, tag, argc-(optind+2), argv+optind+2);
555 }