local.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  1. /*
  2. Copyright (c) 2003-2006 by Juliusz Chroboczek
  3. Permission is hereby granted, free of charge, to any person obtaining a copy
  4. of this software and associated documentation files (the "Software"), to deal
  5. in the Software without restriction, including without limitation the rights
  6. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. copies of the Software, and to permit persons to whom the Software is
  8. furnished to do so, subject to the following conditions:
  9. The above copyright notice and this permission notice shall be included in
  10. all copies or substantial portions of the Software.
  11. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  12. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  13. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  14. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  15. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  16. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  17. THE SOFTWARE.
  18. */
  19. #include "polipo.h"
  20. int disableLocalInterface = 0;
  21. int disableConfiguration = 0;
  22. int disableIndexing = 1;
  23. int disableServersList = 1;
  24. AtomPtr atomInitForbidden;
  25. AtomPtr atomReopenLog;
  26. AtomPtr atomDiscardObjects;
  27. AtomPtr atomWriteoutObjects;
  28. AtomPtr atomFreeChunkArenas;
  29. void
  30. preinitLocal()
  31. {
  32. atomInitForbidden = internAtom("init-forbidden");
  33. atomReopenLog = internAtom("reopen-log");
  34. atomDiscardObjects = internAtom("discard-objects");
  35. atomWriteoutObjects = internAtom("writeout-objects");
  36. atomFreeChunkArenas = internAtom("free-chunk-arenas");
  37. /* These should not be settable for obvious reasons */
  38. CONFIG_VARIABLE(disableLocalInterface, CONFIG_BOOLEAN,
  39. "Disable the local configuration pages.");
  40. CONFIG_VARIABLE(disableConfiguration, CONFIG_BOOLEAN,
  41. "Disable reconfiguring Polipo at runtime.");
  42. CONFIG_VARIABLE(disableIndexing, CONFIG_BOOLEAN,
  43. "Disable indexing of the local cache.");
  44. CONFIG_VARIABLE(disableServersList, CONFIG_BOOLEAN,
  45. "Disable the list of known servers.");
  46. }
  47. static void fillSpecialObject(ObjectPtr, void (*)(FILE*, char*), void*);
  48. int
  49. httpLocalRequest(ObjectPtr object, int method, int from, int to,
  50. HTTPRequestPtr requestor, void *closure)
  51. {
  52. if(object->requestor == NULL)
  53. object->requestor = requestor;
  54. if(!disableLocalInterface && urlIsSpecial(object->key, object->key_size))
  55. return httpSpecialRequest(object, method, from, to,
  56. requestor, closure);
  57. if(method >= METHOD_POST) {
  58. abortObject(object, 405, internAtom("Method not allowed"));
  59. } else if(object->flags & OBJECT_INITIAL) {
  60. /* objectFillFromDisk already did the real work but we have to
  61. make sure we don't get into an infinite loop. */
  62. abortObject(object, 404, internAtom("Not found"));
  63. }
  64. object->age = current_time.tv_sec;
  65. object->date = current_time.tv_sec;
  66. object->flags &= ~OBJECT_VALIDATING;
  67. notifyObject(object);
  68. return 1;
  69. }
  70. void
  71. alternatingHttpStyle(FILE *out, char *id)
  72. {
  73. fprintf(out,
  74. "<style type=\"text/css\">\n"
  75. "#%s tbody tr.even td { background-color: #eee; }\n"
  76. "#%s tbody tr.odd td { background-color: #fff; }\n"
  77. "</style>\n", id, id);
  78. }
  79. static void
  80. printConfig(FILE *out, char *dummy)
  81. {
  82. fprintf(out,
  83. "<!DOCTYPE HTML PUBLIC "
  84. "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
  85. "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
  86. "<html><head>\n"
  87. "<title>Polipo configuration</title>\n"
  88. "</head><body>\n"
  89. "<h1>Polipo configuration</h1>\n");
  90. printConfigVariables(out, 1);
  91. fprintf(out, "<p><a href=\"/polipo/\">back</a></p>");
  92. fprintf(out, "</body></html>\n");
  93. }
  94. #ifndef NO_DISK_CACHE
  95. static void
  96. recursiveIndexDiskObjects(FILE *out, char *root)
  97. {
  98. indexDiskObjects(out, root, 1);
  99. }
  100. static void
  101. plainIndexDiskObjects(FILE *out, char *root)
  102. {
  103. indexDiskObjects(out, root, 0);
  104. }
  105. #endif
  106. static void
  107. serversList(FILE *out, char *dummy)
  108. {
  109. listServers(out);
  110. }
  111. static int
  112. matchUrl(char *base, ObjectPtr object)
  113. {
  114. int n = strlen(base);
  115. if(object->key_size < n)
  116. return 0;
  117. if(memcmp(base, object->key, n) != 0)
  118. return 0;
  119. return (object->key_size == n) || (((char*)object->key)[n] == '?');
  120. }
  121. int
  122. httpSpecialRequest(ObjectPtr object, int method, int from, int to,
  123. HTTPRequestPtr requestor, void *closure)
  124. {
  125. char buffer[1024];
  126. int hlen;
  127. if(method >= METHOD_POST) {
  128. return httpSpecialSideRequest(object, method, from, to,
  129. requestor, closure);
  130. }
  131. if(!(object->flags & OBJECT_INITIAL)) {
  132. privatiseObject(object, 0);
  133. supersedeObject(object);
  134. object->flags &= ~(OBJECT_VALIDATING | OBJECT_INPROGRESS);
  135. notifyObject(object);
  136. return 1;
  137. }
  138. hlen = snnprintf(buffer, 0, 1024,
  139. "\r\nServer: polipo"
  140. "\r\nContent-Type: text/html");
  141. object->date = current_time.tv_sec;
  142. object->age = current_time.tv_sec;
  143. object->headers = internAtomN(buffer, hlen);
  144. object->code = 200;
  145. object->message = internAtom("Okay");
  146. object->flags &= ~OBJECT_INITIAL;
  147. object->flags |= OBJECT_DYNAMIC;
  148. if(object->key_size == 8 && memcmp(object->key, "/polipo/", 8) == 0) {
  149. objectPrintf(object, 0,
  150. "<!DOCTYPE HTML PUBLIC "
  151. "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
  152. "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
  153. "<html><head>\n"
  154. "<title>Polipo</title>\n"
  155. "</head><body>\n"
  156. "<h1>Polipo</h1>\n"
  157. "<p><a href=\"status?\">Status report</a>.</p>\n"
  158. "<p><a href=\"config?\">Current configuration</a>.</p>\n"
  159. "<p><a href=\"servers?\">Known servers</a>.</p>\n"
  160. #ifndef NO_DISK_CACHE
  161. "<p><a href=\"index?\">Disk cache index</a>.</p>\n"
  162. #endif
  163. "</body></html>\n");
  164. object->length = object->size;
  165. } else if(matchUrl("/polipo/status", object)) {
  166. objectPrintf(object, 0,
  167. "<!DOCTYPE HTML PUBLIC "
  168. "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
  169. "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
  170. "<html><head>\n"
  171. "<title>Polipo status report</title>\n"
  172. "</head><body>\n"
  173. "<h1>Polipo proxy on %s:%d: status report</h1>\n"
  174. "<p>The %s proxy on %s:%d is %s.</p>\n"
  175. "<p>There are %d public and %d private objects "
  176. "currently in memory using %d KB in %d chunks "
  177. "(%d KB allocated).</p>\n"
  178. "<p>There are %d atoms.</p>"
  179. "<p><form method=POST action=\"/polipo/status?\">"
  180. "<input type=submit name=\"init-forbidden\" "
  181. "value=\"Read forbidden file\"></form>\n"
  182. "<form method=POST action=\"/polipo/status?\">"
  183. "<input type=submit name=\"writeout-objects\" "
  184. "value=\"Write out in-memory cache\"></form>\n"
  185. "<form method=POST action=\"/polipo/status?\">"
  186. "<input type=submit name=\"discard-objects\" "
  187. "value=\"Discard in-memory cache\"></form>\n"
  188. "<form method=POST action=\"/polipo/status?\">"
  189. "<input type=submit name=\"reopen-log\" "
  190. "value=\"Reopen log file\"></form>\n"
  191. "<form method=POST action=\"/polipo/status?\">"
  192. "<input type=submit name=\"free-chunk-arenas\" "
  193. "value=\"Free chunk arenas\"></form></p>\n"
  194. "<p><a href=\"/polipo/\">back</a></p>"
  195. "</body></html>\n",
  196. proxyName->string, proxyPort,
  197. cacheIsShared ? "shared" : "private",
  198. proxyName->string, proxyPort,
  199. proxyOffline ? "off line" :
  200. (relaxTransparency ?
  201. "on line (transparency relaxed)" :
  202. "on line"),
  203. publicObjectCount, privateObjectCount,
  204. used_chunks * CHUNK_SIZE / 1024, used_chunks,
  205. totalChunkArenaSize() / 1024,
  206. used_atoms);
  207. object->expires = current_time.tv_sec;
  208. object->length = object->size;
  209. } else if(matchUrl("/polipo/config", object)) {
  210. fillSpecialObject(object, printConfig, NULL);
  211. object->expires = current_time.tv_sec + 5;
  212. #ifndef NO_DISK_CACHE
  213. } else if(matchUrl("/polipo/index", object)) {
  214. int len;
  215. char *root;
  216. if(disableIndexing) {
  217. abortObject(object, 403, internAtom("Action not allowed"));
  218. notifyObject(object);
  219. return 1;
  220. }
  221. len = MAX(0, object->key_size - 14);
  222. root = strdup_n((char*)object->key + 14, len);
  223. if(root == NULL) {
  224. abortObject(object, 503, internAtom("Couldn't allocate root"));
  225. notifyObject(object);
  226. return 1;
  227. }
  228. writeoutObjects(1);
  229. fillSpecialObject(object, plainIndexDiskObjects, root);
  230. free(root);
  231. object->expires = current_time.tv_sec + 5;
  232. } else if(matchUrl("/polipo/recursive-index", object)) {
  233. int len;
  234. char *root;
  235. if(disableIndexing) {
  236. abortObject(object, 403, internAtom("Action not allowed"));
  237. notifyObject(object);
  238. return 1;
  239. }
  240. len = MAX(0, object->key_size - 24);
  241. root = strdup_n((char*)object->key + 24, len);
  242. if(root == NULL) {
  243. abortObject(object, 503, internAtom("Couldn't allocate root"));
  244. notifyObject(object);
  245. return 1;
  246. }
  247. writeoutObjects(1);
  248. fillSpecialObject(object, recursiveIndexDiskObjects, root);
  249. free(root);
  250. object->expires = current_time.tv_sec + 20;
  251. #endif
  252. } else if(matchUrl("/polipo/servers", object)) {
  253. if(disableServersList) {
  254. abortObject(object, 403, internAtom("Action not allowed"));
  255. notifyObject(object);
  256. return 1;
  257. }
  258. fillSpecialObject(object, serversList, NULL);
  259. object->expires = current_time.tv_sec + 2;
  260. } else {
  261. abortObject(object, 404, internAtom("Not found"));
  262. }
  263. object->flags &= ~OBJECT_VALIDATING;
  264. notifyObject(object);
  265. return 1;
  266. }
  267. int
  268. httpSpecialSideRequest(ObjectPtr object, int method, int from, int to,
  269. HTTPRequestPtr requestor, void *closure)
  270. {
  271. HTTPConnectionPtr client = requestor->connection;
  272. assert(client->request == requestor);
  273. if(method != METHOD_POST) {
  274. httpClientError(requestor, 405, internAtom("Method not allowed"));
  275. requestor->connection->flags &= ~CONN_READER;
  276. return 1;
  277. }
  278. if(requestor->flags & REQUEST_WAIT_CONTINUE) {
  279. httpClientError(requestor, 417, internAtom("Expectation failed"));
  280. requestor->connection->flags &= ~CONN_READER;
  281. return 1;
  282. }
  283. return httpSpecialDoSide(requestor);
  284. }
  285. int
  286. httpSpecialDoSide(HTTPRequestPtr requestor)
  287. {
  288. HTTPConnectionPtr client = requestor->connection;
  289. if(client->reqlen - client->reqbegin >= client->bodylen) {
  290. AtomPtr data;
  291. data = internAtomN(client->reqbuf + client->reqbegin,
  292. client->reqlen - client->reqbegin);
  293. client->reqbegin = 0;
  294. client->reqlen = 0;
  295. if(data == NULL) {
  296. do_log(L_ERROR, "Couldn't allocate data.\n");
  297. httpClientError(requestor, 500,
  298. internAtom("Couldn't allocate data"));
  299. return 1;
  300. }
  301. httpSpecialDoSideFinish(data, requestor);
  302. return 1;
  303. }
  304. if(client->reqlen - client->reqbegin >= CHUNK_SIZE) {
  305. httpClientError(requestor, 500, internAtom("POST too large"));
  306. return 1;
  307. }
  308. if(client->reqbegin > 0 && client->reqlen > client->reqbegin) {
  309. memmove(client->reqbuf, client->reqbuf + client->reqbegin,
  310. client->reqlen - client->reqbegin);
  311. }
  312. client->reqlen -= client->reqbegin;
  313. client->reqbegin = 0;
  314. do_stream(IO_READ | IO_NOTNOW, client->fd,
  315. client->reqlen, client->reqbuf, CHUNK_SIZE,
  316. httpSpecialClientSideHandler, client);
  317. return 1;
  318. }
  319. int
  320. httpSpecialClientSideHandler(int status,
  321. FdEventHandlerPtr event,
  322. StreamRequestPtr srequest)
  323. {
  324. HTTPConnectionPtr connection = srequest->data;
  325. HTTPRequestPtr request = connection->request;
  326. int push;
  327. if((request->object->flags & OBJECT_ABORTED) ||
  328. !(request->object->flags & OBJECT_INPROGRESS)) {
  329. httpClientDiscardBody(connection);
  330. httpClientError(request, 503, internAtom("Post aborted"));
  331. return 1;
  332. }
  333. if(status < 0) {
  334. do_log_error(L_ERROR, -status, "Reading from client");
  335. if(status == -EDOGRACEFUL)
  336. httpClientFinish(connection, 1);
  337. else
  338. httpClientFinish(connection, 2);
  339. return 1;
  340. }
  341. push = MIN(srequest->offset - connection->reqlen,
  342. connection->bodylen - connection->reqoffset);
  343. if(push > 0) {
  344. connection->reqlen += push;
  345. httpSpecialDoSide(request);
  346. }
  347. do_log(L_ERROR, "Incomplete client request.\n");
  348. connection->flags &= ~CONN_READER;
  349. httpClientRawError(connection, 502,
  350. internAtom("Incomplete client request"), 1);
  351. return 1;
  352. }
  353. int
  354. httpSpecialDoSideFinish(AtomPtr data, HTTPRequestPtr requestor)
  355. {
  356. ObjectPtr object = requestor->object;
  357. if(matchUrl("/polipo/config", object)) {
  358. AtomListPtr list = NULL;
  359. int i, rc;
  360. if(disableConfiguration) {
  361. abortObject(object, 403, internAtom("Action not allowed"));
  362. goto out;
  363. }
  364. list = urlDecode(data->string, data->length);
  365. if(list == NULL) {
  366. abortObject(object, 400,
  367. internAtom("Couldn't parse variable to set"));
  368. goto out;
  369. }
  370. for(i = 0; i < list->length; i++) {
  371. rc = parseConfigLine(list->list[i]->string, NULL, 0, 1);
  372. if(rc < 0) {
  373. abortObject(object, 400,
  374. rc == -1 ?
  375. internAtom("Couldn't parse variable to set") :
  376. internAtom("Variable is not settable"));
  377. destroyAtomList(list);
  378. goto out;
  379. }
  380. }
  381. destroyAtomList(list);
  382. object->date = current_time.tv_sec;
  383. object->age = current_time.tv_sec;
  384. object->headers = internAtom("\r\nLocation: /polipo/config?");
  385. object->code = 303;
  386. object->message = internAtom("Done");
  387. object->flags &= ~OBJECT_INITIAL;
  388. object->length = 0;
  389. } else if(matchUrl("/polipo/status", object)) {
  390. AtomListPtr list = NULL;
  391. int i;
  392. if(disableConfiguration) {
  393. abortObject(object, 403, internAtom("Action not allowed"));
  394. goto out;
  395. }
  396. list = urlDecode(data->string, data->length);
  397. if(list == NULL) {
  398. abortObject(object, 400,
  399. internAtom("Couldn't parse action"));
  400. goto out;
  401. }
  402. for(i = 0; i < list->length; i++) {
  403. char *equals =
  404. memchr(list->list[i]->string, '=', list->list[i]->length);
  405. AtomPtr name =
  406. equals ?
  407. internAtomN(list->list[i]->string,
  408. equals - list->list[i]->string) :
  409. retainAtom(list->list[i]);
  410. if(name == atomInitForbidden)
  411. initForbidden();
  412. else if(name == atomReopenLog)
  413. reopenLog();
  414. else if(name == atomDiscardObjects)
  415. discardObjects(1, 0);
  416. else if(name == atomWriteoutObjects)
  417. writeoutObjects(1);
  418. else if(name == atomFreeChunkArenas)
  419. free_chunk_arenas();
  420. else {
  421. abortObject(object, 400, internAtomF("Unknown action %s",
  422. name->string));
  423. releaseAtom(name);
  424. destroyAtomList(list);
  425. goto out;
  426. }
  427. releaseAtom(name);
  428. }
  429. destroyAtomList(list);
  430. object->date = current_time.tv_sec;
  431. object->age = current_time.tv_sec;
  432. object->headers = internAtom("\r\nLocation: /polipo/status?");
  433. object->code = 303;
  434. object->message = internAtom("Done");
  435. object->flags &= ~OBJECT_INITIAL;
  436. object->length = 0;
  437. } else {
  438. abortObject(object, 405, internAtom("Method not allowed"));
  439. }
  440. out:
  441. releaseAtom(data);
  442. notifyObject(object);
  443. requestor->connection->flags &= ~CONN_READER;
  444. return 1;
  445. }
  446. #ifdef HAVE_FORK
  447. static void
  448. fillSpecialObject(ObjectPtr object, void (*fn)(FILE*, char*), void* closure)
  449. {
  450. int rc;
  451. int filedes[2];
  452. pid_t pid;
  453. sigset_t ss, old_mask;
  454. if(object->flags & OBJECT_INPROGRESS)
  455. return;
  456. rc = pipe(filedes);
  457. if(rc < 0) {
  458. do_log_error(L_ERROR, errno, "Couldn't create pipe");
  459. abortObject(object, 503,
  460. internAtomError(errno, "Couldn't create pipe"));
  461. return;
  462. }
  463. fflush(stdout);
  464. fflush(stderr);
  465. flushLog();
  466. /* Block signals that we handle specially until the child can
  467. disable the handlers. */
  468. interestingSignals(&ss);
  469. /* I'm a little confused. POSIX doesn't allow EINTR here, but I
  470. think that both Linux and SVR4 do. */
  471. do {
  472. rc = sigprocmask(SIG_BLOCK, &ss, &old_mask);
  473. } while (rc < 0 && errno == EINTR);
  474. if(rc < 0) {
  475. do_log_error(L_ERROR, errno, "Sigprocmask failed");
  476. abortObject(object, 503, internAtomError(errno, "Sigprocmask failed"));
  477. close(filedes[0]);
  478. close(filedes[1]);
  479. return;
  480. }
  481. pid = fork();
  482. if(pid < 0) {
  483. do_log_error(L_ERROR, errno, "Couldn't fork");
  484. abortObject(object, 503, internAtomError(errno, "Couldn't fork"));
  485. close(filedes[0]);
  486. close(filedes[1]);
  487. do {
  488. rc = sigprocmask(SIG_SETMASK, &old_mask, NULL);
  489. } while (rc < 0 && errno == EINTR);
  490. if(rc < 0) {
  491. do_log_error(L_ERROR, errno, "Couldn't restore signal mask");
  492. polipoExit();
  493. }
  494. return;
  495. }
  496. if(pid > 0) {
  497. SpecialRequestPtr request;
  498. close(filedes[1]);
  499. do {
  500. rc = sigprocmask(SIG_SETMASK, &old_mask, NULL);
  501. } while (rc < 0 && errno == EINTR);
  502. if(rc < 0) {
  503. do_log_error(L_ERROR, errno, "Couldn't restore signal mask");
  504. polipoExit();
  505. return;
  506. }
  507. request = malloc(sizeof(SpecialRequestRec));
  508. if(request == NULL) {
  509. kill(pid, SIGTERM);
  510. close(filedes[0]);
  511. abortObject(object, 503,
  512. internAtom("Couldn't allocate request\n"));
  513. notifyObject(object);
  514. return;
  515. } else {
  516. request->buf = get_chunk();
  517. if(request->buf == NULL) {
  518. kill(pid, SIGTERM);
  519. close(filedes[0]);
  520. free(request);
  521. abortObject(object, 503,
  522. internAtom("Couldn't allocate request\n"));
  523. notifyObject(object);
  524. return;
  525. }
  526. }
  527. object->flags |= OBJECT_INPROGRESS;
  528. retainObject(object);
  529. request->object = object;
  530. request->fd = filedes[0];
  531. request->pid = pid;
  532. request->offset = 0;
  533. /* Under any sensible scheduler, the child will run before the
  534. parent. So no need for IO_NOTNOW. */
  535. do_stream(IO_READ, filedes[0], 0, request->buf, CHUNK_SIZE,
  536. specialRequestHandler, request);
  537. } else {
  538. /* child */
  539. close(filedes[0]);
  540. uninitEvents();
  541. do {
  542. rc = sigprocmask(SIG_SETMASK, &old_mask, NULL);
  543. } while (rc < 0 && errno == EINTR);
  544. if(rc < 0)
  545. exit(1);
  546. if(filedes[1] != 1)
  547. dup2(filedes[1], 1);
  548. (*fn)(stdout, closure);
  549. exit(0);
  550. }
  551. }
  552. int
  553. specialRequestHandler(int status,
  554. FdEventHandlerPtr event, StreamRequestPtr srequest)
  555. {
  556. SpecialRequestPtr request = srequest->data;
  557. int rc;
  558. int killed = 0;
  559. if(status < 0) {
  560. kill(request->pid, SIGTERM);
  561. killed = 1;
  562. request->object->flags &= ~OBJECT_INPROGRESS;
  563. abortObject(request->object, 502,
  564. internAtomError(-status, "Couldn't read from client"));
  565. goto done;
  566. }
  567. if(srequest->offset > 0) {
  568. rc = objectAddData(request->object, request->buf,
  569. request->offset, srequest->offset);
  570. if(rc < 0) {
  571. kill(request->pid, SIGTERM);
  572. killed = 1;
  573. request->object->flags &= ~OBJECT_INPROGRESS;
  574. abortObject(request->object, 503,
  575. internAtom("Couldn't add data to connection"));
  576. goto done;
  577. }
  578. request->offset += srequest->offset;
  579. }
  580. if(status) {
  581. request->object->flags &= ~OBJECT_INPROGRESS;
  582. request->object->length = request->object->size;
  583. goto done;
  584. }
  585. /* If we're the only person interested in this object, let's abort
  586. it now. */
  587. if(request->object->refcount <= 1) {
  588. kill(request->pid, SIGTERM);
  589. killed = 1;
  590. request->object->flags &= ~OBJECT_INPROGRESS;
  591. abortObject(request->object, 500, internAtom("Aborted"));
  592. goto done;
  593. }
  594. notifyObject(request->object);
  595. do_stream(IO_READ | IO_NOTNOW, request->fd, 0, request->buf, CHUNK_SIZE,
  596. specialRequestHandler, request);
  597. return 1;
  598. done:
  599. close(request->fd);
  600. dispose_chunk(request->buf);
  601. releaseNotifyObject(request->object);
  602. /* That's a blocking wait. It shouldn't block for long, as we've
  603. either already killed the child, or else we got EOF from it. */
  604. do {
  605. rc = waitpid(request->pid, &status, 0);
  606. } while(rc < 0 && errno == EINTR);
  607. if(rc < 0) {
  608. do_log(L_ERROR, "Wait for %d: %d\n", (int)request->pid, errno);
  609. } else {
  610. int normal =
  611. (WIFEXITED(status) && WEXITSTATUS(status) == 0) ||
  612. (killed && WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM);
  613. char *reason =
  614. WIFEXITED(status) ? "with status" :
  615. WIFSIGNALED(status) ? "on signal" :
  616. "with unknown status";
  617. int value =
  618. WIFEXITED(status) ? WEXITSTATUS(status) :
  619. WIFSIGNALED(status) ? WTERMSIG(status) :
  620. status;
  621. do_log(normal ? D_CHILD : L_ERROR,
  622. "Child %d exited %s %d.\n",
  623. (int)request->pid, reason, value);
  624. }
  625. free(request);
  626. return 1;
  627. }
  628. #else
  629. static void
  630. fillSpecialObject(ObjectPtr object, void (*fn)(FILE*, char*), void* closure)
  631. {
  632. FILE *tmp = NULL;
  633. char *buf = NULL;
  634. int rc, len, offset;
  635. if(object->flags & OBJECT_INPROGRESS)
  636. return;
  637. buf = get_chunk();
  638. if(buf == NULL) {
  639. abortObject(object, 503, internAtom("Couldn't allocate chunk"));
  640. goto done;
  641. }
  642. tmp = tmpfile();
  643. if(tmp == NULL) {
  644. abortObject(object, 503, internAtom(pstrerror(errno)));
  645. goto done;
  646. }
  647. (*fn)(tmp, closure);
  648. fflush(tmp);
  649. rewind(tmp);
  650. offset = 0;
  651. while(1) {
  652. len = fread(buf, 1, CHUNK_SIZE, tmp);
  653. if(len <= 0 && ferror(tmp)) {
  654. abortObject(object, 503, internAtom(pstrerror(errno)));
  655. goto done;
  656. }
  657. if(len <= 0)
  658. break;
  659. rc = objectAddData(object, buf, offset, len);
  660. if(rc < 0) {
  661. abortObject(object, 503, internAtom("Couldn't add data to object"));
  662. goto done;
  663. }
  664. offset += len;
  665. }
  666. object->length = offset;
  667. done:
  668. if(buf)
  669. dispose_chunk(buf);
  670. if(tmp)
  671. fclose(tmp);
  672. notifyObject(object);
  673. }
  674. #endif