0231ac85b9805aee9532e94f6bedd128b9fba177
[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
39 #define ENSC_WRAPPERS_FCNTL     1
40 #define ENSC_WRAPPERS_IO        1
41 #define ENSC_WRAPPERS_UNISTD    1
42 #define ENSC_WRAPPERS_VSERVER   1
43 #include <wrappers.h>
44
45 typedef enum { tgNONE,tgCONTEXT, tgRUNNING,
46                tgVDIR, tgNAME, tgCFGDIR, tgAPPDIR,
47                tgAPIVER,
48                tgINITPID, tgINITPID_PID,
49                tgXID, tgUTS, tgSYSINFO,
50                tgFEATURE,
51 }       VserverTag;
52
53 static struct {
54     char const * const  tag;
55     VserverTag const    val;
56     char const * const  descr;
57 }  const TAGS[] = {
58   { "CONTEXT", tgCONTEXT, ("the current and/or assigned context; when an optinal argument "
59                            "evaluates to false,only the current context will be printed") },
60   { "RUNNING", tgRUNNING, "gives out '1' when vserver is running; else, it fails without output" },
61   { "VDIR",    tgVDIR,    "gives out the root-directory of the vserver" },
62   { "NAME",    tgNAME,    "gives out the name of the vserver" },
63   { "CFGDIR",  tgCFGDIR,  "gives out the configuration directory of the vserver" },
64   { "APPDIR",  tgAPPDIR,  "gives out the name of the toplevel application cfgdir" },
65   { "INITPID",     tgINITPID,     "gives out the initpid of the given context" },
66   { "INITPID_PID", tgINITPID_PID, "gives out the initpid of the given pid" },
67   { "XID",         tgXID,         "gives out the context-id of the given pid" },
68   { "APIVER",      tgAPIVER,      "gives out the version of the kernel API" },
69   { "UTS",         tgUTS,         ("gives out an uts-entry; possible entries are "
70                                    "context, sysname, nodename, release, version, "
71                                    "machine and domainname") },
72   { "SYSINFO",     tgSYSINFO,     "gives out information about the systen" },
73   { "FEATURE",     tgFEATURE,     "returns 0 iff the queried feature is supported" },
74 };
75
76 int wrapper_exit_code = 1;
77
78 static struct option const
79 CMDLINE_OPTIONS[] = {
80   { "help",     no_argument,  0, 'h' },
81   { "version",  no_argument,  0, 'v' },
82   { 0,0,0,0 }
83 };
84
85
86 static void
87 showHelp(int fd, char const *cmd, int res)
88 {
89   WRITE_MSG(fd, "Usage:  ");
90   WRITE_STR(fd, cmd);
91   WRITE_MSG(fd,
92             " [-ql] <vserver>|<pid>|<context> <tag>\n"
93             "Please report bugs to " PACKAGE_BUGREPORT "\n");
94   exit(res);
95 }
96
97 static void
98 showVersion()
99 {
100   WRITE_MSG(1,
101             "vserver-info " VERSION " -- returns information about vservers\n"
102             "This program is part of " PACKAGE_STRING "\n\n"
103             "Copyright (C) 2003 Enrico Scholz\n"
104             VERSION_COPYRIGHT_DISCLAIMER);
105   exit(0);
106 }
107
108 static void
109 showTags()
110 {
111   char const *          delim = "";
112   size_t        i;
113
114   WRITE_MSG(1, "Valid tags are: ");
115   for (i=0; i<DIM_OF(TAGS); ++i) {
116     WRITE_STR(1, delim);
117     WRITE_STR(1, TAGS[i].tag);
118
119     delim = ", ";
120   }
121   WRITE_MSG(1, "\n");
122   exit(0);
123 }
124
125 static VserverTag
126 stringToTag(char const *str)
127 {
128   size_t        i;
129   for (i=0; i<DIM_OF(TAGS); ++i)
130     if (strcmp(TAGS[i].tag, str)==0) return TAGS[i].val;
131
132   return tgNONE;
133 }
134
135 static vc_uts_type
136 utsText2Tag(char const *str)
137 {
138   if      (strcmp(str, "context")   ==0) return vcVHI_CONTEXT;
139   else if (strcmp(str, "sysname")   ==0) return vcVHI_SYSNAME;
140   else if (strcmp(str, "nodename")  ==0) return vcVHI_NODENAME;
141   else if (strcmp(str, "release")   ==0) return vcVHI_RELEASE;
142   else if (strcmp(str, "version")   ==0) return vcVHI_VERSION;
143   else if (strcmp(str, "machine")   ==0) return vcVHI_MACHINE;
144   else if (strcmp(str, "domainname")==0) return vcVHI_DOMAINNAME;
145   else {
146     WRITE_MSG(2, "Unknown UTS tag\n");
147     exit(1);
148   }
149 }
150
151 static char *
152 getAPIVer(char *buf)
153 {
154   int           v = vc_get_version();
155   size_t        l;
156   
157   if (v==-1) return 0;
158
159   
160   l = utilvserver_fmt_xulong(0, (unsigned int)v);
161   memcpy(buf, "0x00000000", 10);
162   utilvserver_fmt_xulong(buf+2+8-l, (unsigned int)v);
163
164   return buf;
165 }
166
167 static char *
168 getXid(char *buf, char const *vserver)
169 {
170   pid_t         pid = atoi(vserver);
171   xid_t         xid = vc_get_task_xid(pid);
172
173   if (xid==VC_NOCTX) perror("vc_get_task_xid()");
174   else {
175     utilvserver_fmt_long(buf, xid);
176     return buf;
177   }
178
179   return 0;
180 }
181
182 static char *
183 getInitPid_native(char *buf, xid_t xid)
184 {
185   struct vc_vx_info             info;
186   
187   if (vc_get_vx_info(xid, &info)==-1) perror("vc_get_vx_info()");
188   else {
189     utilvserver_fmt_long(buf, info.initpid);
190     return buf;
191   }
192
193   return 0;
194 }
195
196 #if defined(VC_ENABLE_API_COMPAT) || defined(VC_ENABLE_API_V11)
197 static int
198 selectPid(struct dirent const *ent)
199 {
200   return atoi(ent->d_name)!=0;
201 }
202
203 static bool
204 getInitPid_internal(pid_t pid, xid_t xid, pid_t *res)
205 {
206   *res = -1;
207   
208   for (;*res==-1;) {
209     size_t                      bufsize = utilvserver_getProcEntryBufsize();
210     char                        buf[bufsize+1];
211     char                        *pos = 0;
212
213     pos = utilvserver_getProcEntry(pid, "\ns_context: ", buf, bufsize);
214     if (pos==0 && errno==EAGAIN) continue;
215
216     if (pos==0 || (xid_t)atoi(pos)!=xid) return false;
217
218     buf[bufsize] = '\0';
219     pos          = strstr(buf, "\ninitpid: ");
220     
221     if (pos!=0) {
222       pos         += sizeof("\ninitpid: ")-1;
223       if (strncmp(pos, "none", 4)==0) *res = -1;
224       else                            *res = atoi(pos);
225     }
226   }
227
228   return true;
229 }
230
231 static char *
232 getInitPid_emulated(char *buf, xid_t xid)
233 {
234   struct dirent **namelist;
235   int           n;
236   
237   vc_new_s_context(1,0,0);      // ignore errors silently...
238   n = scandir("/proc", &namelist, selectPid, alphasort);
239   if (n<0) perror("scandir()");
240   else while (n--) {
241     pid_t       pid;
242     if (!getInitPid_internal(atoi(namelist[n]->d_name), xid, &pid)) continue;
243
244     utilvserver_fmt_long(buf, pid);
245     return buf;
246   }
247
248   return 0;
249 }
250 #endif // VC_ENABLE_API_COMPAT
251
252 static char *
253 getInitPid(char *buf, xid_t xid)
254 {
255   if (vc_isSupported(vcFEATURE_VINFO))
256     return getInitPid_native(buf, xid);
257   else
258     return getInitPid_emulated(buf, xid);
259 }
260
261 static char *
262 getInitPidPid(char *buf, char const *vserver)
263 {
264   struct vc_vx_info     info;
265   pid_t                 pid = atoi(vserver);
266   xid_t                 xid = vc_get_task_xid(pid);
267
268   if (xid==VC_NOCTX) perror("vc_get_task_xid()");
269   else if (vc_get_vx_info(xid, &info)==-1) perror("vc_get_vx_info()");
270   else {
271     utilvserver_fmt_long(buf, info.initpid);
272     return buf;
273   }
274
275   return 0;
276 }
277
278 static char *
279 getUTS(char *buf, xid_t xid, size_t argc, char * argv[])
280 {
281   if (argc>0) {
282     vc_uts_type type = utsText2Tag(argv[0]);
283     if (vc_get_vhi_name(xid, type, buf, sizeof(buf)-1)==-1)
284       perror("vc_get_vhi_name()");
285     else
286       return buf;
287   }
288   else {
289     bool                is_passed = false;
290     char                tmp[128];
291 #define APPEND_UTS(TYPE)                                                \
292     (((vc_get_vhi_name(xid, TYPE, tmp, sizeof(tmp)-1)!=-1) && (strcat(buf, tmp), strcat(buf, " "), is_passed=true)) || \
293      (strcat(buf, "??? ")))
294
295     if (APPEND_UTS(vcVHI_CONTEXT) &&
296         APPEND_UTS(vcVHI_SYSNAME) &&
297         APPEND_UTS(vcVHI_NODENAME) &&
298         APPEND_UTS(vcVHI_RELEASE) &&
299         APPEND_UTS(vcVHI_VERSION) &&
300         APPEND_UTS(vcVHI_MACHINE) &&
301         APPEND_UTS(vcVHI_DOMAINNAME) &&
302         is_passed)
303       return buf;
304
305     perror("vc_get_vhi_name()");
306 #undef APPEND_UTS
307   }
308
309   return 0;
310 }
311
312 static int
313 printSysInfo(char *buf)
314 {
315   int                   fd = open(PKGLIBDIR "/FEATURES.txt", O_RDONLY);
316   struct utsname        uts;
317
318   if (uname(&uts)==-1)
319     perror("uname()");
320   else {
321     WRITE_MSG(1,
322               "Versions:\n"
323               "                   Kernel: ");
324     WRITE_STR(1, uts.release);
325     WRITE_MSG(1, "\n"
326               "                   VS-API: ");
327
328     memset(buf, 0, 128);
329     if (getAPIVer(buf)) WRITE_STR(1, buf);
330     else                WRITE_MSG(1, "???");
331     
332     WRITE_MSG(1, "\n"
333               "             util-vserver: " PACKAGE_VERSION "; " __DATE__ ", " __TIME__"\n"
334               "\n");
335   }
336
337   if (fd==-1)
338     WRITE_MSG(1, "FEATURES.txt not found\n");
339   else {
340     off_t               l  = Elseek(fd, 0, SEEK_END);
341     Elseek(fd, 0, SEEK_SET);
342     {
343       char              buf[l];
344       EreadAll(fd, buf, l);
345       EwriteAll(1, buf, l);
346     }
347     Eclose(fd);
348   }
349
350   return EXIT_SUCCESS;
351 }
352
353 static char *
354 getContext(char *buf, char const *vserver, bool allow_only_static)
355 {
356   xid_t         xid = vc_getVserverCtx(vserver, vcCFG_AUTO,
357                                        allow_only_static, 0);
358   if (xid==VC_NOCTX) return 0;
359   
360   utilvserver_fmt_long(buf, xid);
361   return buf;
362 }
363
364 static int
365 testFeature(int argc, char *argv[])
366 {
367   return (argc>0 && vc_isSupportedString(argv[0])) ? EXIT_SUCCESS : EXIT_FAILURE;
368 }
369
370 static bool
371 str2bool(char const *str)
372 {
373   return atoi(str)!=0 || strchr("yYtY", str[0])!=0;
374 }
375
376 static int
377 execQuery(char const *vserver, VserverTag tag, int argc, char *argv[])
378 {
379   char const *          res = 0;
380   char                  buf[sizeof(xid_t)*4 + 1024];
381   xid_t                 xid = *vserver!='\0' ? vc_xidopt2xid(vserver,true,0) : VC_SAMECTX;
382
383   memset(buf, 0, sizeof buf);
384   switch (tag) {
385     case tgNAME         :  res = vc_getVserverName(vserver, vcCFG_AUTO); break;
386     case tgVDIR         :
387       res = vc_getVserverVdir(vserver, vcCFG_AUTO, argc>0 && atoi(argv[0]));
388       break;
389     case tgCFGDIR       :  res = vc_getVserverCfgDir(vserver, vcCFG_AUTO);     break;
390     case tgAPPDIR       :
391       res = vc_getVserverAppDir(vserver, vcCFG_AUTO, argc==0 ? "" : argv[0]);
392       break;
393       
394     case tgRUNNING      :
395       res = (vc_getVserverCtx(vserver, vcCFG_AUTO, false, 0)==VC_NOCTX) ? 0 : "1";
396       break;
397
398     case tgCONTEXT      :  res = getContext(buf, vserver,
399                                             argc==0 || str2bool(argv[0])); break;
400     case tgXID          :  res = getXid(buf, vserver);         break;
401     case tgINITPID      :  res = getInitPid(buf, xid);         break;
402     case tgINITPID_PID  :  res = getInitPidPid(buf, vserver);  break;
403     case tgAPIVER       :  res = getAPIVer(buf);               break;
404     case tgUTS          :  res = getUTS(buf, xid, argc, argv); break;
405     case tgSYSINFO      :  return printSysInfo(buf);           break;
406     case tgFEATURE      :  return testFeature(argc,argv);      break;
407     
408     default             :  assert(false); abort();  // TODO
409   }
410
411   if (res==0) return EXIT_FAILURE;
412   WRITE_STR(1, res);
413   WRITE_MSG(1, "\n");
414   return EXIT_SUCCESS;
415 }
416
417 int main(int argc, char *argv[])
418 {
419   bool          quiet = false;
420   char const *  vserver;
421   VserverTag    tag;
422   
423   while (1) {
424     int         c = getopt_long(argc, argv, "ql", CMDLINE_OPTIONS, 0);
425     if (c==-1) break;
426
427     switch (c) {
428       case 'h'          :  showHelp(1, argv[0], 0);
429       case 'v'          :  showVersion();
430       case 'l'          :  showTags();
431       case 'q'          :  quiet = true; break;
432       default           :
433         WRITE_MSG(2, "Try '");
434         WRITE_STR(2, argv[0]);
435         WRITE_MSG(2, " --help\" for more information.\n");
436         exit(1);
437         break;
438     }
439   }
440
441   if (optind+2>argc) {
442     WRITE_MSG(2, "No vserver or tag give; please try '--help' for more information.\n");
443     exit(1);
444   }
445
446   vserver = argv[optind];
447   tag     = stringToTag(argv[optind+1]);
448
449   if (tag==tgNONE) {
450     WRITE_MSG(2, "Unknown tag; use '-l' to get list of valid tags\n");
451     exit(1);
452   }
453
454   if (quiet) {
455     int         fd = Eopen("/dev/null", O_WRONLY, 0644);
456     Edup2(fd, 1);
457     Eclose(fd);
458   }
459
460   return execQuery(vserver, tag, argc-(optind+2), argv+optind+2);
461 }