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