utilunix.c 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378
  1. /*
  2. Various utilities - Unix variants
  3. Copyright (C) 1994-2015
  4. Free Software Foundation, Inc.
  5. Written by:
  6. Miguel de Icaza, 1994, 1995, 1996
  7. Janne Kukonlehto, 1994, 1995, 1996
  8. Dugan Porter, 1994, 1995, 1996
  9. Jakub Jelinek, 1994, 1995, 1996
  10. Mauricio Plaza, 1994, 1995, 1996
  11. The mc_realpath routine is mostly from uClibc package, written
  12. by Rick Sladkey <jrs@world.std.com>
  13. This file is part of the Midnight Commander.
  14. The Midnight Commander is free software: you can redistribute it
  15. and/or modify it under the terms of the GNU General Public License as
  16. published by the Free Software Foundation, either version 3 of the License,
  17. or (at your option) any later version.
  18. The Midnight Commander is distributed in the hope that it will be useful,
  19. but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. GNU General Public License for more details.
  22. You should have received a copy of the GNU General Public License
  23. along with this program. If not, see <http://www.gnu.org/licenses/>.
  24. */
  25. /** \file utilunix.c
  26. * \brief Source: various utilities - Unix variant
  27. */
  28. #include <config.h>
  29. #include <ctype.h>
  30. #include <errno.h>
  31. #include <limits.h>
  32. #include <signal.h>
  33. #include <stdarg.h>
  34. #include <stdio.h>
  35. #include <stdlib.h>
  36. #include <string.h>
  37. #include <fcntl.h>
  38. #ifdef HAVE_SYS_PARAM_H
  39. #include <sys/param.h>
  40. #endif
  41. #include <sys/types.h>
  42. #include <sys/stat.h>
  43. #ifdef HAVE_SYS_SELECT_H
  44. #include <sys/select.h>
  45. #endif
  46. #include <sys/wait.h>
  47. #ifdef HAVE_SYS_IOCTL_H
  48. #include <sys/ioctl.h>
  49. #endif
  50. #ifdef HAVE_GET_PROCESS_STATS
  51. #include <sys/procstats.h>
  52. #endif
  53. #include <pwd.h>
  54. #include <grp.h>
  55. #include "lib/global.h"
  56. #include "lib/unixcompat.h"
  57. #include "lib/vfs/vfs.h" /* VFS_ENCODING_PREFIX */
  58. #include "lib/strutil.h" /* str_move() */
  59. #include "lib/util.h"
  60. #include "lib/widget.h" /* message() */
  61. #include "lib/vfs/xdirentry.h"
  62. #ifdef HAVE_CHARSET
  63. #include "lib/charsets.h"
  64. #endif
  65. #include "utilunix.h"
  66. /*** global variables ****************************************************************************/
  67. struct sigaction startup_handler;
  68. /*** file scope macro definitions ****************************************************************/
  69. #define UID_CACHE_SIZE 200
  70. #define GID_CACHE_SIZE 30
  71. /* Pipes are guaranteed to be able to hold at least 4096 bytes */
  72. /* More than that would be unportable */
  73. #define MAX_PIPE_SIZE 4096
  74. /*** file scope type declarations ****************************************************************/
  75. typedef struct
  76. {
  77. int index;
  78. char *string;
  79. } int_cache;
  80. typedef enum
  81. {
  82. FORK_ERROR = -1,
  83. FORK_CHILD,
  84. FORK_PARENT,
  85. } my_fork_state_t;
  86. typedef struct
  87. {
  88. struct sigaction intr;
  89. struct sigaction quit;
  90. struct sigaction stop;
  91. } my_system_sigactions_t;
  92. /*** file scope variables ************************************************************************/
  93. static int_cache uid_cache[UID_CACHE_SIZE];
  94. static int_cache gid_cache[GID_CACHE_SIZE];
  95. static int error_pipe[2]; /* File descriptors of error pipe */
  96. static int old_error; /* File descriptor of old standard error */
  97. /*** file scope functions ************************************************************************/
  98. /* --------------------------------------------------------------------------------------------- */
  99. static char *
  100. i_cache_match (int id, int_cache * cache, int size)
  101. {
  102. int i;
  103. for (i = 0; i < size; i++)
  104. if (cache[i].index == id)
  105. return cache[i].string;
  106. return 0;
  107. }
  108. /* --------------------------------------------------------------------------------------------- */
  109. static void
  110. i_cache_add (int id, int_cache * cache, int size, char *text, int *last)
  111. {
  112. g_free (cache[*last].string);
  113. cache[*last].string = g_strdup (text);
  114. cache[*last].index = id;
  115. *last = ((*last) + 1) % size;
  116. }
  117. /* --------------------------------------------------------------------------------------------- */
  118. static my_fork_state_t
  119. my_fork (void)
  120. {
  121. pid_t pid;
  122. pid = fork ();
  123. if (pid < 0)
  124. {
  125. fprintf (stderr, "\n\nfork () = -1\n");
  126. return FORK_ERROR;
  127. }
  128. if (pid == 0)
  129. return FORK_CHILD;
  130. while (TRUE)
  131. {
  132. int status = 0;
  133. if (waitpid (pid, &status, 0) > 0)
  134. return WEXITSTATUS (status) == 0 ? FORK_PARENT : FORK_ERROR;
  135. if (errno != EINTR)
  136. return FORK_ERROR;
  137. }
  138. }
  139. /* --------------------------------------------------------------------------------------------- */
  140. static void
  141. my_system__save_sigaction_handlers (my_system_sigactions_t * sigactions)
  142. {
  143. struct sigaction ignore;
  144. memset (&ignore, 0, sizeof (ignore));
  145. ignore.sa_handler = SIG_IGN;
  146. sigemptyset (&ignore.sa_mask);
  147. sigaction (SIGINT, &ignore, &sigactions->intr);
  148. sigaction (SIGQUIT, &ignore, &sigactions->quit);
  149. /* Restore the original SIGTSTP handler, we don't want ncurses' */
  150. /* handler messing the screen after the SIGCONT */
  151. sigaction (SIGTSTP, &startup_handler, &sigactions->stop);
  152. }
  153. /* --------------------------------------------------------------------------------------------- */
  154. static void
  155. my_system__restore_sigaction_handlers (my_system_sigactions_t * sigactions)
  156. {
  157. sigaction (SIGINT, &sigactions->intr, NULL);
  158. sigaction (SIGQUIT, &sigactions->quit, NULL);
  159. sigaction (SIGTSTP, &sigactions->stop, NULL);
  160. }
  161. /* --------------------------------------------------------------------------------------------- */
  162. static GPtrArray *
  163. my_system_make_arg_array (int flags, const char *shell, char **execute_name)
  164. {
  165. GPtrArray *args_array;
  166. args_array = g_ptr_array_new ();
  167. if ((flags & EXECUTE_AS_SHELL) != 0)
  168. {
  169. g_ptr_array_add (args_array, g_strdup (shell));
  170. g_ptr_array_add (args_array, g_strdup ("-c"));
  171. *execute_name = g_strdup (shell);
  172. }
  173. else
  174. {
  175. char *shell_token;
  176. shell_token = shell != NULL ? strchr (shell, ' ') : NULL;
  177. if (shell_token == NULL)
  178. *execute_name = g_strdup (shell);
  179. else
  180. *execute_name = g_strndup (shell, (gsize) (shell_token - shell));
  181. g_ptr_array_add (args_array, g_strdup (shell));
  182. }
  183. return args_array;
  184. }
  185. /* --------------------------------------------------------------------------------------------- */
  186. static void
  187. mc_pread_stream (mc_pipe_stream_t * ps, const fd_set * fds)
  188. {
  189. size_t buf_len;
  190. ssize_t read_len;
  191. if (!FD_ISSET (ps->fd, fds))
  192. {
  193. ps->len = MC_PIPE_STREAM_UNREAD;
  194. return;
  195. }
  196. buf_len = (size_t) ps->len;
  197. if (buf_len >= MC_PIPE_BUFSIZE)
  198. buf_len = ps->null_term ? MC_PIPE_BUFSIZE - 1 : MC_PIPE_BUFSIZE;
  199. do
  200. {
  201. read_len = read (ps->fd, ps->buf, buf_len);
  202. }
  203. while (read_len < 0 && errno == EINTR);
  204. if (read_len < 0)
  205. {
  206. /* reading error */
  207. ps->len = MC_PIPE_ERROR_READ;
  208. ps->error = errno;
  209. }
  210. else if (read_len == 0)
  211. /* EOF */
  212. ps->len = MC_PIPE_STREAM_EOF;
  213. else
  214. {
  215. /* success */
  216. ps->len = read_len;
  217. if (ps->null_term)
  218. ps->buf[(size_t) ps->len] = '\0';
  219. }
  220. }
  221. /* --------------------------------------------------------------------------------------------- */
  222. /*** public functions ****************************************************************************/
  223. /* --------------------------------------------------------------------------------------------- */
  224. char *
  225. get_owner (int uid)
  226. {
  227. struct passwd *pwd;
  228. char *name;
  229. static int uid_last;
  230. name = i_cache_match (uid, uid_cache, UID_CACHE_SIZE);
  231. if (name != NULL)
  232. return name;
  233. pwd = getpwuid (uid);
  234. if (pwd != NULL)
  235. {
  236. i_cache_add (uid, uid_cache, UID_CACHE_SIZE, pwd->pw_name, &uid_last);
  237. return pwd->pw_name;
  238. }
  239. else
  240. {
  241. static char ibuf[10];
  242. g_snprintf (ibuf, sizeof (ibuf), "%d", uid);
  243. return ibuf;
  244. }
  245. }
  246. /* --------------------------------------------------------------------------------------------- */
  247. char *
  248. get_group (int gid)
  249. {
  250. struct group *grp;
  251. char *name;
  252. static int gid_last;
  253. name = i_cache_match (gid, gid_cache, GID_CACHE_SIZE);
  254. if (name != NULL)
  255. return name;
  256. grp = getgrgid (gid);
  257. if (grp != NULL)
  258. {
  259. i_cache_add (gid, gid_cache, GID_CACHE_SIZE, grp->gr_name, &gid_last);
  260. return grp->gr_name;
  261. }
  262. else
  263. {
  264. static char gbuf[10];
  265. g_snprintf (gbuf, sizeof (gbuf), "%d", gid);
  266. return gbuf;
  267. }
  268. }
  269. /* --------------------------------------------------------------------------------------------- */
  270. /* Since ncurses uses a handler that automatically refreshes the */
  271. /* screen after a SIGCONT, and we don't want this behavior when */
  272. /* spawning a child, we save the original handler here */
  273. void
  274. save_stop_handler (void)
  275. {
  276. sigaction (SIGTSTP, NULL, &startup_handler);
  277. }
  278. /* --------------------------------------------------------------------------------------------- */
  279. /**
  280. * Wrapper for _exit() system call.
  281. * The _exit() function has gcc's attribute 'noreturn', and this is reason why we can't
  282. * mock the call.
  283. *
  284. * @param status exit code
  285. */
  286. void
  287. my_exit (int status)
  288. {
  289. _exit (status);
  290. }
  291. /* --------------------------------------------------------------------------------------------- */
  292. /**
  293. * Call external programs.
  294. *
  295. * @parameter flags addition conditions for running external programs.
  296. * @parameter shell shell (if flags contain EXECUTE_AS_SHELL), command to run otherwise.
  297. * Shell (or command) will be found in paths described in PATH variable
  298. * (if shell parameter doesn't begin from path delimiter)
  299. * @parameter command Command for shell (or first parameter for command, if flags contain EXECUTE_AS_SHELL)
  300. * @return 0 if successfull, -1 otherwise
  301. */
  302. int
  303. my_system (int flags, const char *shell, const char *command)
  304. {
  305. return my_systeml (flags, shell, command, NULL);
  306. }
  307. /* --------------------------------------------------------------------------------------------- */
  308. /**
  309. * Call external programs with various parameters number.
  310. *
  311. * @parameter flags addition conditions for running external programs.
  312. * @parameter shell shell (if flags contain EXECUTE_AS_SHELL), command to run otherwise.
  313. * Shell (or command) will be found in pathes described in PATH variable
  314. * (if shell parameter doesn't begin from path delimiter)
  315. * @parameter ... Command for shell with addition parameters for shell
  316. * (or parameters for command, if flags contain EXECUTE_AS_SHELL).
  317. * Should be NULL terminated.
  318. * @return 0 if successfull, -1 otherwise
  319. */
  320. int
  321. my_systeml (int flags, const char *shell, ...)
  322. {
  323. GPtrArray *args_array;
  324. int status = 0;
  325. va_list vargs;
  326. char *one_arg;
  327. args_array = g_ptr_array_new ();
  328. va_start (vargs, shell);
  329. while ((one_arg = va_arg (vargs, char *)) != NULL)
  330. g_ptr_array_add (args_array, one_arg);
  331. va_end (vargs);
  332. g_ptr_array_add (args_array, NULL);
  333. status = my_systemv_flags (flags, shell, (char *const *) args_array->pdata);
  334. g_ptr_array_free (args_array, TRUE);
  335. return status;
  336. }
  337. /* --------------------------------------------------------------------------------------------- */
  338. /**
  339. * Call external programs with array of strings as parameters.
  340. *
  341. * @parameter command command to run. Command will be found in paths described in PATH variable
  342. * (if command parameter doesn't begin from path delimiter)
  343. * @parameter argv Array of strings (NULL-terminated) with parameters for command
  344. * @return 0 if successfull, -1 otherwise
  345. */
  346. int
  347. my_systemv (const char *command, char *const argv[])
  348. {
  349. my_fork_state_t fork_state;
  350. int status = 0;
  351. my_system_sigactions_t sigactions;
  352. my_system__save_sigaction_handlers (&sigactions);
  353. fork_state = my_fork ();
  354. switch (fork_state)
  355. {
  356. case FORK_ERROR:
  357. status = -1;
  358. break;
  359. case FORK_CHILD:
  360. {
  361. signal (SIGINT, SIG_DFL);
  362. signal (SIGQUIT, SIG_DFL);
  363. signal (SIGTSTP, SIG_DFL);
  364. signal (SIGCHLD, SIG_DFL);
  365. execvp (command, argv);
  366. my_exit (127); /* Exec error */
  367. }
  368. break;
  369. default:
  370. status = 0;
  371. break;
  372. }
  373. my_system__restore_sigaction_handlers (&sigactions);
  374. return status;
  375. }
  376. /* --------------------------------------------------------------------------------------------- */
  377. /**
  378. * Call external programs with flags and with array of strings as parameters.
  379. *
  380. * @parameter flags addition conditions for running external programs.
  381. * @parameter command shell (if flags contain EXECUTE_AS_SHELL), command to run otherwise.
  382. * Shell (or command) will be found in paths described in PATH variable
  383. * (if shell parameter doesn't begin from path delimiter)
  384. * @parameter argv Array of strings (NULL-terminated) with parameters for command
  385. * @return 0 if successfull, -1 otherwise
  386. */
  387. int
  388. my_systemv_flags (int flags, const char *command, char *const argv[])
  389. {
  390. char *execute_name = NULL;
  391. GPtrArray *args_array;
  392. int status = 0;
  393. args_array = my_system_make_arg_array (flags, command, &execute_name);
  394. for (; argv != NULL && *argv != NULL; argv++)
  395. g_ptr_array_add (args_array, *argv);
  396. g_ptr_array_add (args_array, NULL);
  397. status = my_systemv (execute_name, (char *const *) args_array->pdata);
  398. g_free (execute_name);
  399. g_ptr_array_free (args_array, TRUE);
  400. return status;
  401. }
  402. /* --------------------------------------------------------------------------------------------- */
  403. /**
  404. * Create pipe and run child process.
  405. *
  406. * @parameter command command line of child process
  407. * @paremeter error contains pointer to object to handle error code and message
  408. *
  409. * @return newly created object of mc_pipe_t class in success, NULL otherwise
  410. */
  411. mc_pipe_t *
  412. mc_popen (const char *command, GError ** error)
  413. {
  414. mc_pipe_t *p;
  415. char **argv;
  416. p = g_try_new (mc_pipe_t, 1);
  417. if (p == NULL)
  418. {
  419. mc_replace_error (error, MC_PIPE_ERROR_CREATE_PIPE, "%s",
  420. _("Cannot create pipe descriptor"));
  421. goto ret_err;
  422. }
  423. if (!g_shell_parse_argv (command, NULL, &argv, error))
  424. {
  425. mc_replace_error (error, MC_PIPE_ERROR_PARSE_COMMAND, "%s",
  426. _("Cannot parse command for pipe"));
  427. goto ret_err;
  428. }
  429. if (!g_spawn_async_with_pipes (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL,
  430. &p->child_pid, NULL, &p->out.fd, &p->err.fd, error))
  431. {
  432. mc_replace_error (error, MC_PIPE_ERROR_CREATE_PIPE_STREAM, "%s",
  433. _("Cannot create pipe streams"));
  434. goto ret_err;
  435. }
  436. g_strfreev (argv);
  437. p->out.buf[0] = '\0';
  438. p->out.len = MC_PIPE_BUFSIZE;
  439. p->out.null_term = FALSE;
  440. p->err.buf[0] = '\0';
  441. p->err.len = MC_PIPE_BUFSIZE;
  442. p->err.null_term = FALSE;
  443. return p;
  444. ret_err:
  445. g_free (p);
  446. return NULL;
  447. }
  448. /* --------------------------------------------------------------------------------------------- */
  449. /**
  450. * Read stdout and stderr of pipe asynchronously.
  451. *
  452. * @parameter p pipe descriptor
  453. *
  454. * The lengths of read data contain in p->out.len and p->err.len.
  455. * Before read, p->xxx.len is an input:
  456. * p->xxx.len > 0: do read stream p->xxx and store data in p->xxx.buf;
  457. * p->xxx.len <= 0: do not read stream p->xxx.
  458. *
  459. * After read, p->xxx.len is an output and contains the following:
  460. * p->xxx.len > 0: an actual length of read data stored in p->xxx.buf;
  461. * p->xxx.len == MC_PIPE_STREAM_EOF: EOF of stream p->xxx;
  462. * p->xxx.len == MC_PIPE_STREAM_UNREAD: stream p->xxx was not read;
  463. * p->xxx.len == MC_PIPE_ERROR_READ: reading error, and p->xxx.errno is set appropriately.
  464. *
  465. * @paremeter error contains pointer to object to handle error code and message
  466. */
  467. void
  468. mc_pread (mc_pipe_t * p, GError ** error)
  469. {
  470. gboolean read_out, read_err;
  471. fd_set fds;
  472. int maxfd = 0;
  473. int res;
  474. if (error != NULL)
  475. *error = NULL;
  476. read_out = p->out.fd >= 0 && p->out.len > 0;
  477. read_err = p->err.fd >= 0 && p->err.len > 0;
  478. if (!read_out && !read_err)
  479. {
  480. p->out.len = MC_PIPE_STREAM_UNREAD;
  481. p->err.len = MC_PIPE_STREAM_UNREAD;
  482. return;
  483. }
  484. FD_ZERO (&fds);
  485. if (read_out)
  486. {
  487. FD_SET (p->out.fd, &fds);
  488. maxfd = p->out.fd;
  489. }
  490. if (read_err)
  491. {
  492. FD_SET (p->err.fd, &fds);
  493. maxfd = max (maxfd, p->err.fd);
  494. }
  495. /* no timeout */
  496. res = select (maxfd + 1, &fds, NULL, NULL, NULL);
  497. if (res < 0 && errno != EINTR)
  498. {
  499. mc_propagate_error (error, MC_PIPE_ERROR_READ,
  500. _
  501. ("Unexpected error in select() reading data from a child process:\n%s"),
  502. unix_error_string (errno));
  503. return;
  504. }
  505. if (read_out)
  506. mc_pread_stream (&p->out, &fds);
  507. else
  508. p->out.len = MC_PIPE_STREAM_UNREAD;
  509. if (read_err)
  510. mc_pread_stream (&p->err, &fds);
  511. else
  512. p->err.len = MC_PIPE_STREAM_UNREAD;
  513. }
  514. /* --------------------------------------------------------------------------------------------- */
  515. /**
  516. * Close pipe and destroy pipe descriptor.
  517. *
  518. * @paremeter p pipe descriptor
  519. * @paremeter error contains pointer to object to handle error code and message
  520. */
  521. void
  522. mc_pclose (mc_pipe_t * p, GError ** error)
  523. {
  524. int res;
  525. if (p->out.fd >= 0)
  526. res = close (p->out.fd);
  527. if (p->err.fd >= 0)
  528. res = close (p->err.fd);
  529. do
  530. {
  531. int status;
  532. res = waitpid (p->child_pid, &status, 0);
  533. }
  534. while (res < 0 && errno == EINTR);
  535. if (res < 0)
  536. mc_replace_error (error, MC_PIPE_ERROR_READ, _("Unexpected error in waitpid():\n%s"),
  537. unix_error_string (errno));
  538. g_free (p);
  539. }
  540. /* --------------------------------------------------------------------------------------------- */
  541. /**
  542. * Perform tilde expansion if possible.
  543. *
  544. * @param directory pointer to the path
  545. *
  546. * @return newly allocated string, even if it's unchanged.
  547. */
  548. char *
  549. tilde_expand (const char *directory)
  550. {
  551. struct passwd *passwd;
  552. const char *p, *q;
  553. if (*directory != '~')
  554. return g_strdup (directory);
  555. p = directory + 1;
  556. /* d = "~" or d = "~/" */
  557. if (*p == '\0' || IS_PATH_SEP (*p))
  558. {
  559. passwd = getpwuid (geteuid ());
  560. q = IS_PATH_SEP (*p) ? p + 1 : "";
  561. }
  562. else
  563. {
  564. q = strchr (p, PATH_SEP);
  565. if (!q)
  566. {
  567. passwd = getpwnam (p);
  568. }
  569. else
  570. {
  571. char *name;
  572. name = g_strndup (p, q - p);
  573. passwd = getpwnam (name);
  574. q++;
  575. g_free (name);
  576. }
  577. }
  578. /* If we can't figure the user name, leave tilde unexpanded */
  579. if (!passwd)
  580. return g_strdup (directory);
  581. return g_strconcat (passwd->pw_dir, PATH_SEP_STR, q, (char *) NULL);
  582. }
  583. /* --------------------------------------------------------------------------------------------- */
  584. /**
  585. * Creates a pipe to hold standard error for a later analysis.
  586. * The pipe can hold 4096 bytes. Make sure no more is written
  587. * or a deadlock might occur.
  588. */
  589. void
  590. open_error_pipe (void)
  591. {
  592. if (pipe (error_pipe) < 0)
  593. {
  594. message (D_NORMAL, _("Warning"), _("Pipe failed"));
  595. }
  596. old_error = dup (STDERR_FILENO);
  597. if (old_error < 0 || close (STDERR_FILENO) != 0 || dup (error_pipe[1]) != STDERR_FILENO)
  598. {
  599. message (D_NORMAL, _("Warning"), _("Dup failed"));
  600. close (error_pipe[0]);
  601. error_pipe[0] = -1;
  602. }
  603. else
  604. {
  605. /*
  606. * Settng stderr in nonblocking mode as we close it earlier, than
  607. * program stops. We try to read some error at program startup,
  608. * but we should not block on it.
  609. *
  610. * TODO: make piped stdin/stderr poll()/select()able to get rid
  611. * of following hack.
  612. */
  613. int fd_flags;
  614. fd_flags = fcntl (error_pipe[0], F_GETFL, NULL);
  615. if (fd_flags != -1)
  616. {
  617. fd_flags |= O_NONBLOCK;
  618. if (fcntl (error_pipe[0], F_SETFL, fd_flags) == -1)
  619. {
  620. /* TODO: handle it somehow */
  621. }
  622. }
  623. }
  624. /* we never write there */
  625. close (error_pipe[1]);
  626. error_pipe[1] = -1;
  627. }
  628. /* --------------------------------------------------------------------------------------------- */
  629. /**
  630. * Close a pipe
  631. *
  632. * @param error '-1' - ignore errors, '0' - display warning, '1' - display error
  633. * @param text is prepended to the error message from the pipe
  634. *
  635. * @return not 0 if an error was displayed
  636. */
  637. int
  638. close_error_pipe (int error, const char *text)
  639. {
  640. const char *title;
  641. char msg[MAX_PIPE_SIZE];
  642. int len = 0;
  643. /* already closed */
  644. if (error_pipe[0] == -1)
  645. return 0;
  646. if (error < 0 || (error > 0 && (error & D_ERROR) != 0))
  647. title = MSG_ERROR;
  648. else
  649. title = _("Warning");
  650. if (old_error >= 0)
  651. {
  652. if (dup2 (old_error, STDERR_FILENO) == -1)
  653. {
  654. if (error < 0)
  655. error = D_ERROR;
  656. message (error, MSG_ERROR, _("Error dup'ing old error pipe"));
  657. return 1;
  658. }
  659. close (old_error);
  660. len = read (error_pipe[0], msg, MAX_PIPE_SIZE - 1);
  661. if (len >= 0)
  662. msg[len] = 0;
  663. close (error_pipe[0]);
  664. error_pipe[0] = -1;
  665. }
  666. if (error < 0)
  667. return 0; /* Just ignore error message */
  668. if (text == NULL)
  669. {
  670. if (len <= 0)
  671. return 0; /* Nothing to show */
  672. /* Show message from pipe */
  673. message (error, title, "%s", msg);
  674. }
  675. else
  676. {
  677. /* Show given text and possible message from pipe */
  678. message (error, title, "%s\n%s", text, msg);
  679. }
  680. return 1;
  681. }
  682. /* --------------------------------------------------------------------------------------------- */
  683. /**
  684. * Canonicalize path, and return a new path. Do everything in place.
  685. * The new path differs from path in:
  686. * Multiple '/'s are collapsed to a single '/'.
  687. * Leading './'s and trailing '/.'s are removed.
  688. * Trailing '/'s are removed.
  689. * Non-leading '../'s and trailing '..'s are handled by removing
  690. * portions of the path.
  691. * Well formed UNC paths are modified only in the local part.
  692. */
  693. void
  694. custom_canonicalize_pathname (char *path, CANON_PATH_FLAGS flags)
  695. {
  696. char *p, *s;
  697. char *lpath = path; /* path without leading UNC part */
  698. const size_t url_delim_len = strlen (VFS_PATH_URL_DELIMITER);
  699. /* Detect and preserve UNC paths: //server/... */
  700. if ((flags & CANON_PATH_GUARDUNC) != 0 && IS_PATH_SEP (path[0]) && IS_PATH_SEP (path[1]))
  701. {
  702. p = path + 2;
  703. while (p[0] != '\0' && !IS_PATH_SEP (p[0]))
  704. p++;
  705. if (IS_PATH_SEP (p[0]) && p > path + 2)
  706. lpath = p;
  707. }
  708. if (!lpath[0] || !lpath[1])
  709. return;
  710. if (flags & CANON_PATH_JOINSLASHES)
  711. {
  712. /* Collapse multiple slashes */
  713. p = lpath;
  714. while (*p)
  715. {
  716. if (IS_PATH_SEP (p[0]) && IS_PATH_SEP (p[1]) && (p == lpath || *(p - 1) != ':'))
  717. {
  718. s = p + 1;
  719. while (IS_PATH_SEP (*(++s)))
  720. ;
  721. str_move (p + 1, s);
  722. }
  723. p++;
  724. }
  725. }
  726. if (flags & CANON_PATH_JOINSLASHES)
  727. {
  728. /* Collapse "/./" -> "/" */
  729. p = lpath;
  730. while (*p)
  731. {
  732. if (IS_PATH_SEP (p[0]) && p[1] == '.' && IS_PATH_SEP (p[2]))
  733. str_move (p, p + 2);
  734. else
  735. p++;
  736. }
  737. }
  738. if (flags & CANON_PATH_REMSLASHDOTS)
  739. {
  740. size_t len;
  741. /* Remove trailing slashes */
  742. p = lpath + strlen (lpath) - 1;
  743. while (p > lpath && IS_PATH_SEP (*p))
  744. {
  745. if (p >= lpath - (url_delim_len + 1)
  746. && strncmp (p - url_delim_len + 1, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
  747. break;
  748. *p-- = 0;
  749. }
  750. /* Remove leading "./" */
  751. if (lpath[0] == '.' && IS_PATH_SEP (lpath[1]))
  752. {
  753. if (lpath[2] == 0)
  754. {
  755. lpath[1] = 0;
  756. return;
  757. }
  758. else
  759. {
  760. str_move (lpath, lpath + 2);
  761. }
  762. }
  763. /* Remove trailing "/" or "/." */
  764. len = strlen (lpath);
  765. if (len < 2)
  766. return;
  767. if (IS_PATH_SEP (lpath[len - 1])
  768. && (len < url_delim_len
  769. || strncmp (lpath + len - url_delim_len, VFS_PATH_URL_DELIMITER,
  770. url_delim_len) != 0))
  771. {
  772. lpath[len - 1] = '\0';
  773. }
  774. else
  775. {
  776. if (lpath[len - 1] == '.' && IS_PATH_SEP (lpath[len - 2]))
  777. {
  778. if (len == 2)
  779. {
  780. lpath[1] = '\0';
  781. return;
  782. }
  783. else
  784. {
  785. lpath[len - 2] = '\0';
  786. }
  787. }
  788. }
  789. }
  790. if (flags & CANON_PATH_REMDOUBLEDOTS)
  791. {
  792. #ifdef HAVE_CHARSET
  793. const size_t enc_prefix_len = strlen (VFS_ENCODING_PREFIX);
  794. #endif /* HAVE_CHARSET */
  795. /* Collapse "/.." with the previous part of path */
  796. p = lpath;
  797. while (p[0] && p[1] && p[2])
  798. {
  799. if (!IS_PATH_SEP (p[0]) || p[1] != '.' || p[2] != '.'
  800. || (!IS_PATH_SEP (p[3]) && p[3] != '\0'))
  801. {
  802. p++;
  803. continue;
  804. }
  805. /* search for the previous token */
  806. s = p - 1;
  807. if (s >= lpath + url_delim_len - 2
  808. && strncmp (s - url_delim_len + 2, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
  809. {
  810. s -= (url_delim_len - 2);
  811. while (s >= lpath && !IS_PATH_SEP (*s--))
  812. ;
  813. }
  814. while (s >= lpath)
  815. {
  816. if (s - url_delim_len > lpath
  817. && strncmp (s - url_delim_len, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
  818. {
  819. char *vfs_prefix = s - url_delim_len;
  820. struct vfs_class *vclass;
  821. while (vfs_prefix > lpath && !IS_PATH_SEP (*--vfs_prefix))
  822. ;
  823. if (IS_PATH_SEP (*vfs_prefix))
  824. vfs_prefix++;
  825. *(s - url_delim_len) = '\0';
  826. vclass = vfs_prefix_to_class (vfs_prefix);
  827. *(s - url_delim_len) = *VFS_PATH_URL_DELIMITER;
  828. if (vclass != NULL)
  829. {
  830. struct vfs_s_subclass *sub = (struct vfs_s_subclass *) vclass->data;
  831. if (sub != NULL && sub->flags & VFS_S_REMOTE)
  832. {
  833. s = vfs_prefix;
  834. continue;
  835. }
  836. }
  837. }
  838. if (IS_PATH_SEP (*s))
  839. break;
  840. s--;
  841. }
  842. s++;
  843. /* If the previous token is "..", we cannot collapse it */
  844. if (s[0] == '.' && s[1] == '.' && s + 2 == p)
  845. {
  846. p += 3;
  847. continue;
  848. }
  849. if (p[3] != 0)
  850. {
  851. if (s == lpath && IS_PATH_SEP (*s))
  852. {
  853. /* "/../foo" -> "/foo" */
  854. str_move (s + 1, p + 4);
  855. }
  856. else
  857. {
  858. /* "token/../foo" -> "foo" */
  859. #ifdef HAVE_CHARSET
  860. if ((strncmp (s, VFS_ENCODING_PREFIX, enc_prefix_len) == 0)
  861. && (is_supported_encoding (s + enc_prefix_len)))
  862. /* special case: remove encoding */
  863. str_move (s, p + 1);
  864. else
  865. #endif /* HAVE_CHARSET */
  866. str_move (s, p + 4);
  867. }
  868. p = (s > lpath) ? s - 1 : s;
  869. continue;
  870. }
  871. /* trailing ".." */
  872. if (s == lpath)
  873. {
  874. /* "token/.." -> "." */
  875. if (!IS_PATH_SEP (lpath[0]))
  876. lpath[0] = '.';
  877. lpath[1] = '\0';
  878. }
  879. else
  880. {
  881. /* "foo/token/.." -> "foo" */
  882. if (s == lpath + 1)
  883. s[0] = '\0';
  884. #ifdef HAVE_CHARSET
  885. else if ((strncmp (s, VFS_ENCODING_PREFIX, enc_prefix_len) == 0)
  886. && (is_supported_encoding (s + enc_prefix_len)))
  887. {
  888. /* special case: remove encoding */
  889. s[0] = '.';
  890. s[1] = '.';
  891. s[2] = '\0';
  892. /* search for the previous token */
  893. /* IS_PATH_SEP (s[-1]) */
  894. p = s - 1;
  895. while (p >= lpath && !IS_PATH_SEP (*p))
  896. p--;
  897. if (p >= lpath)
  898. continue;
  899. }
  900. #endif /* HAVE_CHARSET */
  901. else
  902. {
  903. if (s >= lpath + url_delim_len
  904. && strncmp (s - url_delim_len, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
  905. *s = '\0';
  906. else
  907. s[-1] = '\0';
  908. }
  909. }
  910. break;
  911. }
  912. }
  913. }
  914. /* --------------------------------------------------------------------------------------------- */
  915. void
  916. canonicalize_pathname (char *path)
  917. {
  918. custom_canonicalize_pathname (path, CANON_PATH_ALL);
  919. }
  920. /* --------------------------------------------------------------------------------------------- */
  921. #ifdef HAVE_GET_PROCESS_STATS
  922. int
  923. gettimeofday (struct timeval *tp, void *tzp)
  924. {
  925. return get_process_stats (tp, PS_SELF, 0, 0);
  926. }
  927. #endif /* HAVE_GET_PROCESS_STATS */
  928. /* --------------------------------------------------------------------------------------------- */
  929. #ifndef HAVE_REALPATH
  930. char *
  931. mc_realpath (const char *path, char *resolved_path)
  932. {
  933. char copy_path[PATH_MAX];
  934. char link_path[PATH_MAX];
  935. char got_path[PATH_MAX];
  936. char *new_path = got_path;
  937. char *max_path;
  938. int readlinks = 0;
  939. int n;
  940. /* Make a copy of the source path since we may need to modify it. */
  941. if (strlen (path) >= PATH_MAX - 2)
  942. {
  943. errno = ENAMETOOLONG;
  944. return NULL;
  945. }
  946. strcpy (copy_path, path);
  947. path = copy_path;
  948. max_path = copy_path + PATH_MAX - 2;
  949. /* If it's a relative pathname use getwd for starters. */
  950. if (!IS_PATH_SEP (*path))
  951. {
  952. new_path = g_get_current_dir ();
  953. if (new_path == NULL)
  954. {
  955. strcpy (got_path, "");
  956. }
  957. else
  958. {
  959. g_snprintf (got_path, PATH_MAX, "%s", new_path);
  960. g_free (new_path);
  961. new_path = got_path;
  962. }
  963. new_path += strlen (got_path);
  964. if (!IS_PATH_SEP (new_path[-1]))
  965. *new_path++ = PATH_SEP;
  966. }
  967. else
  968. {
  969. *new_path++ = PATH_SEP;
  970. path++;
  971. }
  972. /* Expand each slash-separated pathname component. */
  973. while (*path != '\0')
  974. {
  975. /* Ignore stray "/". */
  976. if (IS_PATH_SEP (*path))
  977. {
  978. path++;
  979. continue;
  980. }
  981. if (*path == '.')
  982. {
  983. /* Ignore ".". */
  984. if (path[1] == '\0' || IS_PATH_SEP (path[1]))
  985. {
  986. path++;
  987. continue;
  988. }
  989. if (path[1] == '.')
  990. {
  991. if (path[2] == '\0' || IS_PATH_SEP (path[2]))
  992. {
  993. path += 2;
  994. /* Ignore ".." at root. */
  995. if (new_path == got_path + 1)
  996. continue;
  997. /* Handle ".." by backing up. */
  998. while (!IS_PATH_SEP ((--new_path)[-1]))
  999. ;
  1000. continue;
  1001. }
  1002. }
  1003. }
  1004. /* Safely copy the next pathname component. */
  1005. while (*path != '\0' && !IS_PATH_SEP (*path))
  1006. {
  1007. if (path > max_path)
  1008. {
  1009. errno = ENAMETOOLONG;
  1010. return NULL;
  1011. }
  1012. *new_path++ = *path++;
  1013. }
  1014. #ifdef S_IFLNK
  1015. /* Protect against infinite loops. */
  1016. if (readlinks++ > MAXSYMLINKS)
  1017. {
  1018. errno = ELOOP;
  1019. return NULL;
  1020. }
  1021. /* See if latest pathname component is a symlink. */
  1022. *new_path = '\0';
  1023. n = readlink (got_path, link_path, PATH_MAX - 1);
  1024. if (n < 0)
  1025. {
  1026. /* EINVAL means the file exists but isn't a symlink. */
  1027. if (errno != EINVAL)
  1028. {
  1029. /* Make sure it's null terminated. */
  1030. *new_path = '\0';
  1031. strcpy (resolved_path, got_path);
  1032. return NULL;
  1033. }
  1034. }
  1035. else
  1036. {
  1037. /* Note: readlink doesn't add the null byte. */
  1038. link_path[n] = '\0';
  1039. if (IS_PATH_SEP (*link_path))
  1040. /* Start over for an absolute symlink. */
  1041. new_path = got_path;
  1042. else
  1043. /* Otherwise back up over this component. */
  1044. while (!IS_PATH_SEP (*(--new_path)))
  1045. ;
  1046. /* Safe sex check. */
  1047. if (strlen (path) + n >= PATH_MAX - 2)
  1048. {
  1049. errno = ENAMETOOLONG;
  1050. return NULL;
  1051. }
  1052. /* Insert symlink contents into path. */
  1053. strcat (link_path, path);
  1054. strcpy (copy_path, link_path);
  1055. path = copy_path;
  1056. }
  1057. #endif /* S_IFLNK */
  1058. *new_path++ = PATH_SEP;
  1059. }
  1060. /* Delete trailing slash but don't whomp a lone slash. */
  1061. if (new_path != got_path + 1 && IS_PATH_SEP (new_path[-1]))
  1062. new_path--;
  1063. /* Make sure it's null terminated. */
  1064. *new_path = '\0';
  1065. strcpy (resolved_path, got_path);
  1066. return resolved_path;
  1067. }
  1068. #endif /* HAVE_REALPATH */
  1069. /* --------------------------------------------------------------------------------------------- */
  1070. /**
  1071. * Return the index of the permissions triplet
  1072. *
  1073. */
  1074. int
  1075. get_user_permissions (struct stat *st)
  1076. {
  1077. static gboolean initialized = FALSE;
  1078. static gid_t *groups;
  1079. static int ngroups;
  1080. static uid_t uid;
  1081. int i;
  1082. if (!initialized)
  1083. {
  1084. uid = geteuid ();
  1085. ngroups = getgroups (0, NULL);
  1086. if (ngroups == -1)
  1087. ngroups = 0; /* ignore errors */
  1088. /* allocate space for one element in addition to what
  1089. * will be filled by getgroups(). */
  1090. groups = g_new (gid_t, ngroups + 1);
  1091. if (ngroups != 0)
  1092. {
  1093. ngroups = getgroups (ngroups, groups);
  1094. if (ngroups == -1)
  1095. ngroups = 0; /* ignore errors */
  1096. }
  1097. /* getgroups() may or may not return the effective group ID,
  1098. * so we always include it at the end of the list. */
  1099. groups[ngroups++] = getegid ();
  1100. initialized = TRUE;
  1101. }
  1102. if (st->st_uid == uid || uid == 0)
  1103. return 0;
  1104. for (i = 0; i < ngroups; i++)
  1105. {
  1106. if (st->st_gid == groups[i])
  1107. return 1;
  1108. }
  1109. return 2;
  1110. }
  1111. /* --------------------------------------------------------------------------------------------- */
  1112. /**
  1113. * Build filename from arguments.
  1114. * Like to g_build_filename(), but respect VFS_PATH_URL_DELIMITER
  1115. */
  1116. char *
  1117. mc_build_filenamev (const char *first_element, va_list args)
  1118. {
  1119. gboolean absolute;
  1120. const char *element = first_element;
  1121. GString *path;
  1122. char *ret;
  1123. if (element == NULL)
  1124. return NULL;
  1125. path = g_string_new ("");
  1126. absolute = IS_PATH_SEP (*first_element);
  1127. do
  1128. {
  1129. if (*element == '\0')
  1130. element = va_arg (args, char *);
  1131. else
  1132. {
  1133. char *tmp_element;
  1134. size_t len;
  1135. const char *start;
  1136. tmp_element = g_strdup (element);
  1137. element = va_arg (args, char *);
  1138. canonicalize_pathname (tmp_element);
  1139. len = strlen (tmp_element);
  1140. start = IS_PATH_SEP (tmp_element[0]) ? tmp_element + 1 : tmp_element;
  1141. g_string_append (path, start);
  1142. if (!IS_PATH_SEP (tmp_element[len - 1]) && element != NULL)
  1143. g_string_append_c (path, PATH_SEP);
  1144. g_free (tmp_element);
  1145. }
  1146. }
  1147. while (element != NULL);
  1148. if (absolute)
  1149. g_string_prepend_c (path, PATH_SEP);
  1150. ret = g_string_free (path, FALSE);
  1151. canonicalize_pathname (ret);
  1152. return ret;
  1153. }
  1154. /* --------------------------------------------------------------------------------------------- */
  1155. /**
  1156. * Build filename from arguments.
  1157. * Like to g_build_filename(), but respect VFS_PATH_URL_DELIMITER
  1158. */
  1159. char *
  1160. mc_build_filename (const char *first_element, ...)
  1161. {
  1162. va_list args;
  1163. char *ret;
  1164. if (first_element == NULL)
  1165. return NULL;
  1166. va_start (args, first_element);
  1167. ret = mc_build_filenamev (first_element, args);
  1168. va_end (args);
  1169. return ret;
  1170. }
  1171. /* --------------------------------------------------------------------------------------------- */