swiss.c 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. /*++
  2. Copyright (c) 2013 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. swiss.c
  5. Abstract:
  6. This module implements the Swiss utility, which contains many of the
  7. standard commands packaged into one binary.
  8. Author:
  9. Evan Green 5-Jun-2013
  10. Environment:
  11. User Mode
  12. --*/
  13. //
  14. // ------------------------------------------------------------------- Includes
  15. //
  16. #include <minoca/lib/types.h>
  17. #include <assert.h>
  18. #include <errno.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include "swiss.h"
  22. #include "swisscmd.h"
  23. #include "swlib.h"
  24. //
  25. // ---------------------------------------------------------------- Definitions
  26. //
  27. #define SWISS_VERSION_MAJOR 1
  28. #define SWISS_VERSION_MINOR 0
  29. //
  30. // ------------------------------------------------------ Data Type Definitions
  31. //
  32. //
  33. // ----------------------------------------------- Internal Function Prototypes
  34. //
  35. //
  36. // -------------------------------------------------------------------- Globals
  37. //
  38. //
  39. // ------------------------------------------------------------------ Functions
  40. //
  41. INT
  42. main (
  43. INT ArgumentCount,
  44. CHAR **Arguments
  45. )
  46. /*++
  47. Routine Description:
  48. This routine implements the main entry point for the Swiss utility.
  49. Arguments:
  50. ArgumentCount - Supplies the number of arguments on the command line.
  51. Arguments - Supplies an array of pointers to strings representing the
  52. arguments.
  53. Return Value:
  54. 0 on success.
  55. Non-zero on failure.
  56. --*/
  57. {
  58. PSWISS_COMMAND_ENTRY Command;
  59. ULONG CommandIndex;
  60. PSTR CommandName;
  61. PSTR Dot;
  62. id_t EffectiveId;
  63. PSTR LastSlash;
  64. id_t RealId;
  65. BOOL Result;
  66. INT ReturnValue;
  67. SwSetCurrentApplicationName("swiss");
  68. //
  69. // Figure out the command name. Something like c:/mydir/sh.exe should
  70. // become just 'sh'.
  71. //
  72. CommandName = Arguments[0];
  73. LastSlash = strrchr(Arguments[0], '/');
  74. if (LastSlash == NULL) {
  75. LastSlash = strrchr(Arguments[0], '\\');
  76. }
  77. if (LastSlash != NULL) {
  78. LastSlash += 1;
  79. } else {
  80. LastSlash = Arguments[0];
  81. }
  82. CommandName = strdup(LastSlash);
  83. if (CommandName == NULL) {
  84. ReturnValue = 1;
  85. goto mainEnd;
  86. }
  87. Dot = strchr(CommandName, '.');
  88. if (Dot != NULL) {
  89. *Dot = '\0';
  90. }
  91. //
  92. // Look for the command in either the last component of argv[0] or in
  93. // argv[1].
  94. //
  95. Command = SwissFindCommand(CommandName);
  96. if (Command == NULL) {
  97. if (ArgumentCount > 1) {
  98. Command = SwissFindCommand(Arguments[1]);
  99. if (Command != NULL) {
  100. Arguments += 1;
  101. ArgumentCount -= 1;
  102. }
  103. //
  104. // Default to sh if swiss was just run directly with no arguments.
  105. //
  106. } else if (strncasecmp(CommandName, "swiss", 5) == 0) {
  107. Command = SwissFindCommand(SH_COMMAND_NAME);
  108. if (Command != NULL) {
  109. SwPrintVersion(SWISS_VERSION_MAJOR, SWISS_VERSION_MINOR);
  110. }
  111. }
  112. }
  113. //
  114. // This is not a valid command.
  115. //
  116. if (Command == NULL) {
  117. ReturnValue = 1;
  118. //
  119. // The entry was not valid, so look at the next argument. If there is
  120. // no next argument, print the usage and exit.
  121. //
  122. if ((ArgumentCount < 2) || (strcmp(Arguments[1], "--help") == 0)) {
  123. printf("Usage: swiss <command> ...\n\nValid Commands:\n\n");
  124. CommandIndex = 0;
  125. while (SwissCommands[CommandIndex].CommandName != NULL) {
  126. if ((SwissCommands[CommandIndex].Flags &
  127. SWISS_APP_HIDDEN) == 0) {
  128. printf("%10s - %s\n",
  129. SwissCommands[CommandIndex].CommandName,
  130. SwissCommands[CommandIndex].CommandDescription);
  131. }
  132. CommandIndex += 1;
  133. }
  134. printf("\n");
  135. } else if (strcmp(Arguments[1], "--list") == 0) {
  136. CommandIndex = 0;
  137. while (SwissCommands[CommandIndex].CommandName != NULL) {
  138. if ((SwissCommands[CommandIndex].Flags &
  139. SWISS_APP_HIDDEN) == 0) {
  140. printf("%s\n", SwissCommands[CommandIndex].CommandName);
  141. }
  142. CommandIndex += 1;
  143. }
  144. ReturnValue = 0;
  145. } else {
  146. SwPrintError(0,
  147. NULL,
  148. "Command not found in either '%s' nor '%s'",
  149. CommandName,
  150. Arguments[1]);
  151. }
  152. goto mainEnd;
  153. }
  154. //
  155. // Drop setuid privileges unless the app wants to keep them.
  156. //
  157. if ((Command->Flags & SWISS_APP_SETUID_OK) == 0) {
  158. RealId = SwGetRealUserId();
  159. EffectiveId = SwGetEffectiveUserId();
  160. if ((RealId != 0) && (RealId != EffectiveId)) {
  161. ReturnValue = SwSetRealUserId(RealId);
  162. if (ReturnValue != 0) {
  163. SwPrintError(ReturnValue, NULL, "Failed to drop privileges");
  164. ReturnValue = 1;
  165. goto mainEnd;
  166. }
  167. assert(SwGetEffectiveUserId() == RealId);
  168. }
  169. RealId = SwGetRealGroupId();
  170. EffectiveId = SwGetEffectiveGroupId();
  171. if ((RealId != 0) && (RealId != EffectiveId)) {
  172. ReturnValue = SwSetRealGroupId(RealId);
  173. if (ReturnValue != 0) {
  174. SwPrintError(ReturnValue, NULL, "Failed to drop privileges");
  175. ReturnValue = 1;
  176. goto mainEnd;
  177. }
  178. assert(SwGetEffectiveGroupId() == RealId);
  179. }
  180. }
  181. //
  182. // Run the command.
  183. //
  184. Result = SwissRunCommand(Command,
  185. Arguments,
  186. ArgumentCount,
  187. FALSE,
  188. TRUE,
  189. &ReturnValue);
  190. if (Result == FALSE) {
  191. ReturnValue = 1;
  192. goto mainEnd;
  193. }
  194. mainEnd:
  195. if (CommandName != NULL) {
  196. free(CommandName);
  197. }
  198. return ReturnValue;
  199. }
  200. PSWISS_COMMAND_ENTRY
  201. SwissFindCommand (
  202. PSTR Command
  203. )
  204. /*++
  205. Routine Description:
  206. This routine searches for a command in the global command list.
  207. Arguments:
  208. Command - Supplies a pointer to the string of the command to look for.
  209. Return Value:
  210. Returns a pointer to the command entry on success.
  211. NULL if the command could not be found.
  212. --*/
  213. {
  214. ULONG CommandIndex;
  215. //
  216. // Skip the dash which indicates a login process.
  217. //
  218. if (Command[0] == '-') {
  219. Command += 1;
  220. }
  221. CommandIndex = 0;
  222. while (SwissCommands[CommandIndex].CommandName != NULL) {
  223. if (strcmp(Command, SwissCommands[CommandIndex].CommandName) == 0) {
  224. return &(SwissCommands[CommandIndex]);
  225. }
  226. CommandIndex += 1;
  227. }
  228. return NULL;
  229. }
  230. BOOL
  231. SwissRunCommand (
  232. PSWISS_COMMAND_ENTRY Command,
  233. CHAR **Arguments,
  234. ULONG ArgumentCount,
  235. BOOL SeparateProcess,
  236. BOOL Wait,
  237. PINT ReturnValue
  238. )
  239. /*++
  240. Routine Description:
  241. This routine runs a builtin command.
  242. Arguments:
  243. Command - Supplies the command entry to run.
  244. Arguments - Supplies an array of pointers to strings representing the
  245. arguments.
  246. ArgumentCount - Supplies the number of arguments on the command line.
  247. SeparateProcess - Supplies a boolean indicating if the command should be
  248. executed in a separate process space.
  249. Wait - Supplies a boolean indicating if this routine should not return
  250. until the command has completed.
  251. ReturnValue - Supplies a pointer where the return value of the command
  252. will be returned on success.
  253. Return Value:
  254. TRUE on success.
  255. FALSE if the command is not a recognized swiss builtin command.
  256. --*/
  257. {
  258. pid_t Child;
  259. PSTR ExecutablePath;
  260. PSTR OriginalApplication;
  261. INT Result;
  262. pid_t WaitPid;
  263. //
  264. // If the command needs to be run in a separate process, there's one of two
  265. // options. Either fork and run the command directly, or execute the same
  266. // process with the new arguments.
  267. //
  268. if (SeparateProcess != FALSE) {
  269. if (SwForkSupported != 0) {
  270. Child = SwFork();
  271. if (Child < 0) {
  272. return FALSE;
  273. //
  274. // If this is the child, run the command and then exit.
  275. //
  276. } else if (Child == 0) {
  277. OriginalApplication = SwSetCurrentApplicationName(
  278. Command->CommandName);
  279. *ReturnValue = Command->MainFunction(ArgumentCount, Arguments);
  280. exit(*ReturnValue);
  281. //
  282. // If this is the parent, potentially wait for the child.
  283. //
  284. } else {
  285. WaitPid = SwWaitPid(Child, 0, ReturnValue);
  286. if (WaitPid == -1) {
  287. return FALSE;
  288. }
  289. }
  290. //
  291. // Fork is not supported, so run the process again with different
  292. // arguments.
  293. //
  294. } else {
  295. ExecutablePath = SwGetExecutableName();
  296. assert(ExecutablePath != NULL);
  297. Result = SwRunCommand(ExecutablePath,
  298. Arguments,
  299. ArgumentCount,
  300. !Wait,
  301. ReturnValue);
  302. if (Result != 0) {
  303. return FALSE;
  304. }
  305. }
  306. //
  307. // Just run it directly.
  308. //
  309. } else {
  310. //
  311. // Asynchronous but same-process execution is not supported.
  312. //
  313. assert(Wait != FALSE);
  314. OriginalApplication = SwSetCurrentApplicationName(Command->CommandName);
  315. *ReturnValue = Command->MainFunction(ArgumentCount, Arguments);
  316. SwSetCurrentApplicationName(OriginalApplication);
  317. }
  318. fflush(NULL);
  319. return TRUE;
  320. }
  321. //
  322. // --------------------------------------------------------- Internal Functions
  323. //