unix.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. #include <dirent.h>
  2. #include <fcntl.h>
  3. #include <paths.h>
  4. #include <stdlib.h>
  5. #include <sys/wait.h>
  6. #include "soh.h"
  7. #include "unix.h"
  8. /**********************************************************************
  9. This file contains functions for running Unix programs. One of the
  10. paradigms of Unix philosophy sounds like this: "a program should do
  11. one thing and do it well". This program tries to follow this philosophy,
  12. so the main task is to process HTTP requests from the client, and the
  13. rest of the work is delegated to other programs. For example, to send a
  14. file to a client, all you need to do is send it the correct HTTP headers
  15. and call the "cat" program, replacing stdout with the client's socket
  16. descriptor.
  17. ***********************************************************************/
  18. /**************************
  19. * UTILS *
  20. **************************/
  21. int createPipe( int* pipeInput, int* pipeOutput ){
  22. int i;
  23. int pipes[2];
  24. //create pipe
  25. if( pipe(pipes) ){
  26. perror( "create pipes" );
  27. return -1;
  28. }
  29. //set close_on_exec flag on pipe
  30. for( i = 0; i < 2; i++ ) if( fcntl(pipes[i], F_SETFD, FD_CLOEXEC) ){
  31. perror( "set close_on_exec flag on pipe" );
  32. for( i = 0; i < 2; i++ ) close( pipes[i] );
  33. return -1;
  34. }
  35. *pipeInput = pipes[1];
  36. *pipeOutput = pipes[0];
  37. return 0;
  38. }
  39. static struct progdesc* getUnixProgramByAlias( struct instance* this, const char* alias ){
  40. int i;
  41. for( i = 0; this->progs[i].name; i++ ){
  42. if( !this->progs[i].path ) continue;
  43. if( strcmp(alias, this->progs[i].alias) ) continue;
  44. return &this->progs[i];
  45. }
  46. return NULL;
  47. }
  48. /********************************
  49. * RUN PROGRAM *
  50. ********************************/
  51. int unixProgramResult( pid_t programPid, const char* programName ){
  52. int wstatus;
  53. pid_t waitres;
  54. //wait while programs works
  55. waitres = waitpid( programPid, &wstatus, 0 );
  56. if( waitres < 0 ) {
  57. perror( "waitpid" );
  58. return -2;
  59. }
  60. //print result
  61. if( WIFSIGNALED(wstatus) ) {
  62. printf( "%s was killed by signal %d\n", programName, WTERMSIG(wstatus) );
  63. return -1;
  64. }
  65. if( WIFEXITED(wstatus) ){
  66. if( WEXITSTATUS(wstatus) ){
  67. printf( "%s return code %d\n", programName, WEXITSTATUS(wstatus) );
  68. return -1;
  69. }
  70. return 0;
  71. }
  72. printf( "FATAL: waitpid return unknown status.\n" );
  73. return -2;
  74. }
  75. pid_t startUnixProgram( char* argv[], char* envp[], int stdio[3] ){
  76. int i;
  77. int res;
  78. pid_t forkRes;
  79. FILE* stdFiles[] = { stdin, stdout, stderr };
  80. char* errstr[] = { "dup2 stdin", "dup2 stdout", "dup2 stderr" };
  81. //after fork main thread return
  82. //and child be works in this function
  83. forkRes = fork();
  84. if( forkRes ){
  85. if( forkRes < 0 ) perror( "startUnixProgram fork" );
  86. return forkRes;
  87. }
  88. for( i = 0; i < 3; i++ ) if( stdio[i] >= 0 ){
  89. res = dup2( stdio[i], fileno(stdFiles[i]) );
  90. if( res < 0 ){
  91. perror( errstr[i] );
  92. exit( 1 );
  93. }
  94. }
  95. execve( argv[0], argv, envp );
  96. perror( "execve" );
  97. exit( 66 );
  98. }
  99. static int startUnixPrograms( struct instance* this, char** argv[], int count ){
  100. int i;
  101. int res;
  102. int pipeInput;
  103. int pipeOutput;
  104. pid_t pids[count];
  105. int stdio[3] = { -1, -1, -1 };
  106. int programResult;
  107. //init some vars
  108. res = 0;
  109. pipeInput = -1;
  110. pipeOutput = -1;
  111. /********* start programs *********/
  112. for( i = 0; i < count; i++ ){
  113. //if last, then connect prorgamm out to client socket
  114. if( i + 1 == count ) stdio[1] = this->clientSocket;
  115. else{
  116. //create new pipe
  117. res = createPipe( &pipeInput, &pipeOutput );
  118. if( res ){
  119. if( pipeOutput >= 0 ) close( pipeOutput );
  120. goto l_monitor;
  121. }
  122. //connect programm stdout to pipe input
  123. stdio[1] = pipeInput;
  124. }
  125. //start program
  126. pids[i] = startUnixProgram( argv[i], this->envp, stdio );
  127. if( pids[i] < 0 ){
  128. if( pipeInput >= 0 ) close( pipeInput );
  129. if( pipeOutput >= 0 ) close( pipeOutput );
  130. goto l_monitor;
  131. }
  132. //close our copy pipeInput of current pipe
  133. if( pipeInput >= 0 ){
  134. close( pipeInput );
  135. pipeInput = -1;
  136. }
  137. //close our copy pipeOutput of previous pipe
  138. if( stdio[0] >= 0 ) close( stdio[0] );
  139. //connect current pipeOutput to stdin of next programm
  140. stdio[0] = pipeOutput;
  141. }
  142. /********* wait results *********/
  143. l_monitor:
  144. //in case if error occurred while starting programs
  145. if( i != count ){
  146. count = i;
  147. printf( "Start unix programs failed.\n" );
  148. }
  149. //wait results
  150. for( i = 0; i < count; i++ ){
  151. programResult = unixProgramResult( pids[i], argv[i][0] );
  152. if( programResult == -1 ) res = -1;
  153. else if( programResult == -2 ) return -2;
  154. }
  155. return res;
  156. }
  157. /********************************************
  158. * SEARCH PROGRAMS ON HOST *
  159. ********************************************/
  160. static int searchUnixProgramInDir( struct progdesc* prog, DIR* dir, char* path ){
  161. int res;
  162. char tmpbuf[8192];
  163. struct dirent* dirent;
  164. struct stat statbuf;
  165. for( dirent = readdir(dir); dirent; dirent = readdir(dir) ){
  166. //compare name
  167. if( strcmp(prog->name, dirent->d_name) ) continue;
  168. //full path to file
  169. snprintf( tmpbuf, sizeof(tmpbuf), "%s/%s", path, dirent->d_name );
  170. //get file info via stat and check type
  171. res = stat( tmpbuf, &statbuf );
  172. if( res || (statbuf.st_mode & S_IFMT) != S_IFREG ) continue;
  173. //check file is executable
  174. if( !(statbuf.st_mode & S_IXUSR) & !(statbuf.st_mode & S_IXGRP) & !(statbuf.st_mode & S_IXOTH) ) continue;
  175. //set programm path
  176. prog->path = customMalloc( strlen(tmpbuf) + 1 );
  177. if( !prog->path ) return -2;
  178. sprintf( prog->path, "%s", tmpbuf );
  179. //search next program
  180. return 1;
  181. }
  182. return 0;
  183. }
  184. #define PARSE_MAX_PATHS 20
  185. int searchUnixPrograms( struct instance* this ){
  186. int i;
  187. int i2;
  188. int res;
  189. char* ePath;
  190. size_t ePathOffset;
  191. int pathsCount;
  192. char paths[PARSE_MAX_PATHS][256];
  193. DIR* dir;
  194. printf( "Searching for avaliable utils... " );
  195. fflush( stdout );
  196. //init some vars
  197. pathsCount = 0;
  198. ePathOffset = 0;
  199. //get env PATH
  200. ePath = getenv( "PATH" );
  201. if( !ePath ) ePath = _PATH_STDPATH;
  202. /********* parse ePath into paths *********/
  203. for( pathsCount = 0; pathsCount < PARSE_MAX_PATHS; pathsCount++ ){
  204. res = parserCore( ePath, ':', paths[pathsCount], sizeof(paths[pathsCount]), &ePathOffset );
  205. if( res < 0 ){
  206. printf( "%s: Failed to parse PATH environment variable.\n", __FUNCTION__ );
  207. return -2; //error
  208. }
  209. if( !res ) break;
  210. }
  211. /********* search progs *********/
  212. this->progs = unixProgramDB;
  213. //i - progs DB
  214. for( i = 0; unixProgramDB[i].name; i++ ){
  215. //i2 - paths
  216. for( i2 = 0; i2 < pathsCount; i2++ ){
  217. dir = opendir( paths[i2] );
  218. if( !dir ) continue;
  219. res = searchUnixProgramInDir( &unixProgramDB[i], dir, paths[i2] );
  220. closedir( dir );
  221. if( res < 0 ) return -2;
  222. if( res > 0 ) break;
  223. }
  224. }
  225. printf( "done.\n" );
  226. return 0;
  227. }
  228. /*************************************
  229. * DOWNLOAD HANDLER *
  230. *************************************/
  231. static int checkIndexValidity( struct file_db* indexItem ){
  232. int res;
  233. char type;
  234. struct stat statbuf;
  235. //get info about file
  236. res = lstat( indexItem->desc + 1, &statbuf );
  237. if( res ){
  238. perror( "archiver stat" );
  239. return -2;
  240. }
  241. //convert stat type to our type
  242. res = convertStatType( &statbuf, &type );
  243. if( res ) return -2;
  244. //make sure the type in the index matches the type of the file on disk
  245. if( (indexItem->desc[0] & FILEDESC_TYPE_MASK) != type ){
  246. printf( "FATAL: File type in the index does not match with file type on the disk.\n" );
  247. return -2;
  248. }
  249. //for file also check size
  250. if( (indexItem->desc[0] & FILEDESC_TYPE_MASK) != FILEDESC_TYPE_DIR ){
  251. if( indexItem->size != statbuf.st_size ){
  252. printf( "FATAL: Filesize in the index does not match with filesize on the disk.\n" );
  253. return -2;
  254. }
  255. }
  256. return 0;
  257. }
  258. static int getFileMime( struct instance* this, char* filename, char* mimeBuf, int mimeBufLen ){
  259. int res;
  260. int offset;
  261. int pipeInput;
  262. int pipeOutput;
  263. int stdio[3];
  264. struct progdesc* file;
  265. char* args[6];
  266. pid_t filePid;
  267. //init some vars
  268. offset = 0;
  269. //get file util
  270. file = getUnixProgramByAlias( this, "file" );
  271. if( !file ) return -1;
  272. //prepare args
  273. args[0] = file->path;
  274. args[1] = "-i";
  275. args[2] = "-b";
  276. args[3] = "--";
  277. args[4] = filename;
  278. args[5] = NULL;
  279. //create pipe
  280. res = createPipe( &pipeInput, &pipeOutput );
  281. if( res ) return -1;
  282. //route output to pipe
  283. stdio[0] = -1; //default stdin
  284. stdio[1] = pipeInput; //stdout to pipe input
  285. stdio[2] = -1; //default stderr
  286. //start file
  287. filePid = startUnixProgram( args, this->envp, stdio );
  288. close( pipeInput );
  289. if( filePid < 0 ){
  290. close( pipeOutput );
  291. return -1;
  292. }
  293. //read mime from pipe
  294. l_readNext:
  295. res = read( pipeOutput, mimeBuf + offset, mimeBufLen - offset );
  296. if( res > 0 ){
  297. offset += res;
  298. goto l_readNext;
  299. }
  300. //set null-terminator and close pipe
  301. mimeBuf[offset-1] = 0x00;
  302. close( pipeOutput );
  303. return unixProgramResult( filePid, args[0] );
  304. }
  305. int unixDownloadHandler( struct instance* this, struct file_db* requested, char* format ){
  306. int res;
  307. char buf[4096];
  308. char encodedFilename[2048];
  309. int headersLength;
  310. char parsedFormat[8];
  311. size_t formatOffset;
  312. struct progdesc* reader;
  313. struct progdesc* compressor;
  314. int programsCount;
  315. char* readerArgv[6];
  316. char* compressorArgv[2];
  317. char** argvArr[3];
  318. char fileExtension[16];
  319. char flagIsRaw;
  320. char flagIfView;
  321. char mime[1024];
  322. //init some vars
  323. formatOffset = 0;
  324. reader = NULL;
  325. compressor = NULL;
  326. fileExtension[0] = 0x00;
  327. flagIsRaw = 0x00;
  328. flagIfView = 0x00;
  329. //check the validity of the index
  330. res = checkIndexValidity( requested );
  331. if( res ){
  332. httpSendStatus( this, 500, "Internal server error." );
  333. return res;
  334. }
  335. /********* select reader *********/
  336. //if view in browser requested
  337. if( !strcmp("view", format) ){
  338. flagIfView = 0xff;
  339. goto l_raw;
  340. }
  341. //if raw format requested
  342. if( !format[0] ){
  343. l_raw:
  344. //raw format is available only for regular files, remember?
  345. if( (requested->desc[0] & FILEDESC_TYPE_MASK) != FILEDESC_TYPE_FILE ) goto l_501;
  346. reader = getUnixProgramByAlias( this, "raw" );
  347. if( !reader ) goto l_501;
  348. flagIsRaw = 0xff;
  349. goto l_prepare_argv;
  350. }
  351. //parse format
  352. res = parserCore( format, '.', parsedFormat, sizeof(parsedFormat), &formatOffset );
  353. if( res < 0 ) goto l_400;
  354. //search suitable reader
  355. reader = getUnixProgramByAlias( this, parsedFormat );
  356. if( !reader ) goto l_501;
  357. if( reader->type != PROGDESC_TYPE_ARCHIVER ){
  358. //not a regular file can be sent only with the help of archivers, remember?
  359. if( (requested->desc[0] & FILEDESC_TYPE_MASK) != FILEDESC_TYPE_FILE ) goto l_501;
  360. if( reader->type != PROGDESC_TYPE_COMPRESSOR ) goto l_501;
  361. compressor = reader;
  362. reader = getUnixProgramByAlias( this, "raw" );
  363. if( !reader ) goto l_501;
  364. }
  365. /********* select compressor *********/
  366. //parse next part of format and search compressor if need
  367. if( res ){
  368. //e.g. xz.xz
  369. if( compressor ) goto l_400;
  370. res = parserCore( format, '.', parsedFormat, sizeof(parsedFormat), &formatOffset );
  371. if( res < 0 ) goto l_400;
  372. if( res ) goto l_501;// e.g. tar.xz.*
  373. compressor = getUnixProgramByAlias( this, parsedFormat );
  374. if( !compressor ) goto l_501;
  375. if( compressor->type != PROGDESC_TYPE_COMPRESSOR ) goto l_501;
  376. }
  377. /********* file extension *********/
  378. if( reader->type == PROGDESC_TYPE_ARCHIVER ){
  379. if( compressor ){
  380. res = sprintf( fileExtension, ".%s.%s", reader->alias, compressor->alias );
  381. } else {
  382. res = sprintf( fileExtension, ".%s", reader->alias );
  383. }
  384. } else {
  385. res = sprintf( fileExtension, ".%s", compressor->alias );
  386. }
  387. if( res < 0 ) goto l_500;
  388. /********* prepare argv for programs *********/
  389. l_prepare_argv:
  390. printf( "using reader %s\n", reader->name );
  391. if( compressor ) printf( "using compressor %s\n", compressor->name );
  392. programsCount = 1;
  393. //reader argv
  394. readerArgv[0] = reader->path;
  395. if( !strcmp(reader->name, "tar") ){
  396. readerArgv[1] = "-cf";
  397. readerArgv[2] = "-";
  398. readerArgv[3] = "--";
  399. readerArgv[4] = requested->desc + 1;
  400. readerArgv[5] = NULL;
  401. }else{
  402. readerArgv[1] = "--";
  403. readerArgv[2] = requested->desc + 1;
  404. readerArgv[3] = NULL;
  405. }
  406. argvArr[0] = readerArgv;
  407. //compressor argv
  408. if( !compressor ) argvArr[1] = NULL;
  409. else{
  410. compressorArgv[0] = compressor->path;
  411. compressorArgv[1] = NULL;
  412. argvArr[1] = compressorArgv;
  413. argvArr[2] = NULL;
  414. programsCount = 2;
  415. }
  416. /********* send HTTP headers *********/
  417. //encode filename
  418. res = uriEncode( requested->desc + 1, encodedFilename, sizeof(encodedFilename) );
  419. if( res < 0 ) goto l_500;
  420. //if a raw file is requested, then we can send its size
  421. if( flagIsRaw ){
  422. if( flagIfView ){
  423. res = getFileMime( this, requested->desc + 1, mime, 1024 );
  424. if( res == -1 ) goto l_500;
  425. if( res == -2 ){
  426. httpSendStatus( this, 500, "Internal server error." );
  427. return -2;
  428. }
  429. res = sprintf( buf,
  430. "%s 200 OK\r\n" //this->protocol
  431. "Content-Type: %s\r\n" //mime
  432. "Content-Length: %li\r\n" //requested->size
  433. "\r\n",
  434. this->protocol, mime, requested->size
  435. );
  436. } else {
  437. res = sprintf( buf,
  438. "%s 200 OK\r\n" //this->protocol
  439. "Content-Disposition: attachment; filename=\"%s\"\r\n" //encodedFilename
  440. "Content-Length: %li\r\n" //requested->size
  441. "\r\n",
  442. this->protocol, encodedFilename, requested->size
  443. );
  444. }
  445. //if an archiver and/or compressor is used, you can't know the size of the output file
  446. } else {
  447. res = sprintf( buf,
  448. "%s 200 OK\r\n" //this->protocol
  449. "Content-Disposition: attachment; filename=\"%s%s\"\r\n" //name.extension
  450. "\r\n",
  451. this->protocol, encodedFilename, fileExtension
  452. );
  453. }
  454. if( res < 0 ) goto l_500;
  455. headersLength = res;
  456. //send headers
  457. res = sendData( this->clientSocket, buf, headersLength );
  458. if( res ) return -1;
  459. /********* start unix programs *********/
  460. printf( "<<< Uploading %s...\n", requested->desc + 1 );
  461. res = startUnixPrograms( this, argvArr, programsCount );
  462. if( !res ) printf( "Done.\n" );
  463. else printf( "Failed.\n" );
  464. return res;
  465. l_400: return httpSendStatus( this, 400, "Bad request." );
  466. l_500: return httpSendStatus( this, 500, "Internal server error." );
  467. l_501: return httpSendStatus( this, 501, "Not implemented." );
  468. }