server.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. /**
  2. server.c -- AppWeb main program
  3. Copyright (c) All Rights Reserved. See copyright notice at the bottom of the file.
  4. usage: appweb [options]
  5. or: appweb [options] [documents] [[ip][:port] ...]
  6. --chroot dir # Change root to directory (unix only)
  7. --config configFile # Use given config file instead
  8. --debugger # Disable timeouts to make debugging easier
  9. --home path # Set the home working directory
  10. --log logFile:level # Log to file at verbosity level
  11. --trace logFile:level # Trace HTTP requests to file
  12. --name uniqueName # Name for this instance
  13. --version # Output version information
  14. -v # Same as --log stderr:2
  15. */
  16. /********************************* Includes ***********************************/
  17. #include "appweb.h"
  18. #include "esp.h"
  19. /********************************** Locals ************************************/
  20. /*
  21. Global application object. Provides the top level roots of all data objects for the Garbage collector.
  22. Appweb uses garbage collection and all memory must have a reference to stop being reclaimed by the GC.
  23. */
  24. typedef struct AppwebApp {
  25. Mpr *mpr;
  26. cchar *documents;
  27. cchar *home;
  28. cchar *configFile;
  29. char *pathVar;
  30. } AppwebApp;
  31. static AppwebApp *app;
  32. /***************************** Forward Declarations ***************************/
  33. static int changeRoot(cchar *jail);
  34. static int checkEnvironment(cchar *program);
  35. static int createEndpoints(int argc, char **argv);
  36. static int findAppwebConf();
  37. static void manageApp(AppwebApp *app, int flags);
  38. static void usageError();
  39. #if ME_UNIX_LIKE
  40. static int unixSecurityChecks(cchar *program, cchar *home);
  41. #elif ME_WIN_LIKE
  42. static long msgProc(HWND hwnd, uint msg, uint wp, long lp);
  43. #endif
  44. /*********************************** Code *************************************/
  45. MAIN(appweb, int argc, char **argv, char **envp)
  46. {
  47. Mpr *mpr;
  48. cchar *argp, *jail;
  49. char *logSpec, *traceSpec;
  50. int argind, status, verbose;
  51. jail = 0;
  52. verbose = 0;
  53. logSpec = 0;
  54. traceSpec = 0;
  55. if ((mpr = mprCreate(argc, argv, MPR_USER_EVENTS_THREAD)) == NULL) {
  56. exit(1);
  57. }
  58. mprSetAppName(ME_NAME, ME_TITLE, ME_VERSION);
  59. if (httpCreate(HTTP_CLIENT_SIDE | HTTP_SERVER_SIDE) == 0) {
  60. exit(2);
  61. }
  62. /*
  63. Allocate the top level application object. ManageApp is the GC manager function and is called
  64. by the GC to mark references in the app object.
  65. */
  66. if ((app = mprAllocObj(AppwebApp, manageApp)) == NULL) {
  67. exit(3);
  68. }
  69. mprAddRoot(app);
  70. mprAddStandardSignals();
  71. app->mpr = mpr;
  72. app->configFile = sclone("appweb.conf");
  73. app->home = mprGetCurrentPath();
  74. app->documents = app->home;
  75. argc = mpr->argc;
  76. argv = (char**) mpr->argv;
  77. for (argind = 1; argind < argc; argind++) {
  78. argp = argv[argind];
  79. if (*argp != '-') {
  80. break;
  81. }
  82. if (smatch(argp, "--config") || smatch(argp, "--conf")) {
  83. if (argind >= argc) {
  84. usageError();
  85. }
  86. app->configFile = sclone(argv[++argind]);
  87. #if ME_UNIX_LIKE
  88. } else if (smatch(argp, "--chroot")) {
  89. if (argind >= argc) {
  90. usageError();
  91. }
  92. jail = mprGetAbsPath(argv[++argind]);
  93. #endif
  94. } else if (smatch(argp, "--debugger") || smatch(argp, "-D")) {
  95. mprSetDebugMode(1);
  96. } else if (smatch(argp, "--exe")) {
  97. if (argind >= argc) {
  98. usageError();
  99. }
  100. mpr->argv[0] = mprGetAbsPath(argv[++argind]);
  101. mprSetAppPath(mpr->argv[0]);
  102. mprSetModuleSearchPath(NULL);
  103. } else if (smatch(argp, "--home")) {
  104. if (argind >= argc) {
  105. usageError();
  106. }
  107. app->home = mprGetAbsPath(argv[++argind]);
  108. if (chdir(app->home) < 0) {
  109. mprError("%s: Cannot change directory to %s", mprGetAppName(), app->home);
  110. exit(4);
  111. }
  112. } else if (smatch(argp, "--log") || smatch(argp, "-l")) {
  113. if (argind >= argc) {
  114. usageError();
  115. }
  116. logSpec = argv[++argind];
  117. } else if (smatch(argp, "--name") || smatch(argp, "-n")) {
  118. if (argind >= argc) {
  119. usageError();
  120. }
  121. mprSetAppName(argv[++argind], 0, 0);
  122. } else if (smatch(argp, "--trace") || smatch(argp, "-t")) {
  123. if (argind >= argc) {
  124. usageError();
  125. }
  126. traceSpec = argv[++argind];
  127. } else if (smatch(argp, "--verbose") || smatch(argp, "-v")) {
  128. if (!logSpec) {
  129. logSpec = sfmt("stderr:2");
  130. }
  131. if (!traceSpec) {
  132. traceSpec = sfmt("stderr:2");
  133. }
  134. } else if (smatch(argp, "--version") || smatch(argp, "-V")) {
  135. mprPrintf("%s\n", ME_VERSION);
  136. exit(0);
  137. } else if (*argp == '-' && isdigit((uchar) argp[1])) {
  138. if (!logSpec) {
  139. logSpec = sfmt("stderr:%d", (int) stoi(&argp[1]));
  140. }
  141. if (!traceSpec) {
  142. traceSpec = sfmt("stderr:%d", (int) stoi(&argp[1]));
  143. }
  144. } else if (smatch(argp, "-?") || scontains(argp, "-help")) {
  145. usageError();
  146. exit(5);
  147. } else if (*argp == '-') {
  148. mprLog("error appweb", 0, "Unknown switch \"%s\"", argp);
  149. usageError();
  150. exit(5);
  151. }
  152. }
  153. if (logSpec) {
  154. mprStartLogging(logSpec, MPR_LOG_DETAILED | MPR_LOG_CONFIG | MPR_LOG_CMDLINE);
  155. }
  156. if (traceSpec) {
  157. httpStartTracing(traceSpec);
  158. }
  159. /*
  160. Start the multithreaded portable runtime (MPR)
  161. */
  162. if (mprStart() < 0) {
  163. mprError("Cannot start MPR for %s", mprGetAppName());
  164. mprDestroy();
  165. return MPR_ERR_CANT_INITIALIZE;
  166. }
  167. if (checkEnvironment(argv[0]) < 0) {
  168. exit(6);
  169. }
  170. if (findAppwebConf() < 0) {
  171. exit(7);
  172. }
  173. if (jail && changeRoot(jail) < 0) {
  174. exit(8);
  175. }
  176. if (createEndpoints(argc - argind, &argv[argind]) < 0) {
  177. return MPR_ERR_CANT_INITIALIZE;
  178. }
  179. if (httpStartEndpoints() < 0) {
  180. mprError("Cannot start HTTP service, exiting.");
  181. exit(9);
  182. }
  183. mprServiceEvents(-1, 0);
  184. status = mprGetExitStatus();
  185. mprLog("info appweb", 1, "Stopping Appweb ...");
  186. mprDestroy();
  187. return status;
  188. }
  189. static void manageApp(AppwebApp *app, int flags)
  190. {
  191. if (flags & MPR_MANAGE_MARK) {
  192. mprMark(app->documents);
  193. mprMark(app->configFile);
  194. mprMark(app->pathVar);
  195. mprMark(app->home);
  196. }
  197. }
  198. /*
  199. Move into a chroot jail
  200. */
  201. static int changeRoot(cchar *jail)
  202. {
  203. #if ME_UNIX_LIKE
  204. if (chdir(app->home) < 0) {
  205. mprError("%s: Cannot change directory to %s", mprGetAppName(), app->home);
  206. return MPR_ERR_CANT_INITIALIZE;
  207. }
  208. if (chroot(jail) < 0) {
  209. if (errno == EPERM) {
  210. mprError("%s: Must be super user to use the --chroot option", mprGetAppName());
  211. } else {
  212. mprError("%s: Cannot change change root directory to %s, errno %d", mprGetAppName(), jail, errno);
  213. }
  214. return MPR_ERR_CANT_INITIALIZE;
  215. } else {
  216. mprLog("info appweb", 1, "Chroot to: \"%s\"", jail);
  217. app->home = mprGetCurrentPath();
  218. }
  219. #endif
  220. return 0;
  221. }
  222. /*
  223. Create the listening endoints
  224. */
  225. static int createEndpoints(int argc, char **argv)
  226. {
  227. cchar *ip;
  228. int argind, port, secure;
  229. ip = 0;
  230. port = -1;
  231. argind = 0;
  232. if (argc > argind) {
  233. app->documents = sclone(argv[argind++]);
  234. mprLog("info appweb", 2, "Documents %s", app->documents);
  235. }
  236. if (argc == 0) {
  237. if (maConfigureServer(app->configFile, 0, 0, 0, 0) < 0) {
  238. return MPR_ERR_CANT_CREATE;
  239. }
  240. } else {
  241. app->documents = sclone(argv[argind++]);
  242. if (argind == argc) {
  243. if (maConfigureServer(NULL, app->home, app->documents, NULL, ME_HTTP_PORT) < 0) {
  244. return MPR_ERR_CANT_CREATE;
  245. }
  246. } else while (argind < argc) {
  247. mprParseSocketAddress(argv[argind++], &ip, &port, &secure, 80);
  248. if (maConfigureServer(NULL, app->home, app->documents, ip, port) < 0) {
  249. return MPR_ERR_CANT_CREATE;
  250. }
  251. }
  252. }
  253. return 0;
  254. }
  255. /*
  256. Find the appweb.conf file
  257. */
  258. static int findAppwebConf()
  259. {
  260. cchar *userPath;
  261. userPath = app->configFile;
  262. if (app->configFile == 0) {
  263. app->configFile = mprJoinPathExt(mprGetAppName(), ".conf");
  264. }
  265. if (!mprPathExists(app->configFile, R_OK)) {
  266. if (!userPath) {
  267. app->configFile = mprJoinPath(app->home, "appweb.conf");
  268. if (!mprPathExists(app->configFile, R_OK)) {
  269. app->configFile = mprJoinPath(mprGetAppDir(), sfmt("%s.conf", mprGetAppName()));
  270. }
  271. }
  272. if (!mprPathExists(app->configFile, R_OK)) {
  273. mprError("Cannot open config file \"%s\"", app->configFile);
  274. return MPR_ERR_CANT_OPEN;
  275. }
  276. }
  277. return 0;
  278. }
  279. static void usageError(Mpr *mpr)
  280. {
  281. cchar *name;
  282. name = mprGetAppName();
  283. mprEprintf("\n%s Usage:\n\n"
  284. " %s [options]\n"
  285. " %s [options] documents ip[:port] ...\n\n"
  286. " Without [documents ip:port], %s will read the appweb.conf configuration file.\n\n"
  287. " Options:\n"
  288. " --config configFile # Use named config file instead appweb.conf\n"
  289. " --chroot directory # Change root directory to run more securely (Unix)\n"
  290. " --debugger # Disable timeouts to make debugging easier\n"
  291. " --exe path # Set path to Appweb executable on Vxworks\n"
  292. " --home directory # Change to directory to run\n"
  293. " --log logFile:level # Log to file file at verbosity level\n"
  294. " --name uniqueName # Unique name for this instance\n"
  295. " --verbose # Same as --log stderr:2\n"
  296. " --version # Output version information\n\n",
  297. mprGetAppTitle(), name, name, name);
  298. exit(10);
  299. }
  300. /*
  301. Security checks. Make sure we are staring with a safe environment
  302. */
  303. static int checkEnvironment(cchar *program)
  304. {
  305. #if ME_UNIX_LIKE
  306. cchar *home;
  307. home = mprGetCurrentPath();
  308. if (unixSecurityChecks(program, home) < 0) {
  309. return -1;
  310. }
  311. app->pathVar = sjoin("PATH=", getenv("PATH"), ":", mprGetAppDir(), NULL);
  312. putenv(app->pathVar);
  313. #endif
  314. return 0;
  315. }
  316. #if ME_UNIX_LIKE
  317. /*
  318. Security checks. Make sure we are staring with a safe environment
  319. */
  320. static int unixSecurityChecks(cchar *program, cchar *home)
  321. {
  322. struct stat sbuf;
  323. if (((stat(home, &sbuf)) != 0) || !(S_ISDIR(sbuf.st_mode))) {
  324. mprError("Cannot access directory: %s", home);
  325. return MPR_ERR_BAD_STATE;
  326. }
  327. if ((sbuf.st_mode & S_IWOTH) || (sbuf.st_mode & S_IWGRP)) {
  328. mprError("Security risk, %s is writeable by others", home);
  329. }
  330. /*
  331. Should always convert the program name into a fully qualified path. Otherwise this fails.
  332. */
  333. if (*program == '/') {
  334. if ((stat(program, &sbuf)) != 0) {
  335. mprError("Cannot access program: %s", program);
  336. return MPR_ERR_BAD_STATE;
  337. }
  338. if ((sbuf.st_mode & S_IWOTH) || (sbuf.st_mode & S_IWGRP)) {
  339. mprError("Security risk, %s is writeable by others", program);
  340. }
  341. if (sbuf.st_mode & S_ISUID) {
  342. mprError("Security risk, %s is setuid", program);
  343. }
  344. if (sbuf.st_mode & S_ISGID) {
  345. mprError("Security risk, %s is setgid", program);
  346. }
  347. }
  348. return 0;
  349. }
  350. #endif /* ME_UNIX_LIKE */
  351. /*
  352. Copyright (c) Embedthis Software. All Rights Reserved.
  353. This software is distributed under commercial and open source licenses.
  354. You may use the Embedthis Open Source license or you may acquire a
  355. commercial license from Embedthis Software. You agree to be fully bound
  356. by the terms of either license. Consult the LICENSE.md distributed with
  357. this software for full details and other copyrights.
  358. */