*** empty log message ***
[midnight-commander.git] / src / utilunix.c
blob4f2bccdb3152c7fbb7d522f32011d639777641da
1 /* Various utilities - Unix variants
2 Copyright (C) 1994, 1995, 1996 the Free Software Foundation.
3 Written 1994, 1995, 1996 by:
4 Miguel de Icaza, Janne Kukonlehto, Dugan Porter,
5 Jakub Jelinek, Mauricio Plaza.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
21 #include <config.h>
22 #include <stdio.h>
23 #include <sys/types.h>
24 #ifdef HAVE_UNISTD_H
25 # include <unistd.h>
26 #endif
27 #include <fcntl.h>
28 #include <signal.h> /* my_system */
29 #include <limits.h> /* INT_MAX */
30 #include <sys/stat.h>
31 #include <stdarg.h>
32 #include <errno.h> /* my_system */
33 #include <string.h>
34 #include <ctype.h>
35 #ifdef HAVE_SYS_IOCTL_H
36 # include <sys/ioctl.h>
37 #endif
39 #include "global.h"
40 #include "fsusage.h"
41 #include "mountlist.h"
42 #include "dialog.h" /* message() */
43 #include "../vfs/vfs.h" /* mc_read() */
45 struct sigaction startup_handler;
47 #ifndef VFS_STANDALONE
48 /* uid of the MC user */
49 static uid_t current_user_uid = -1;
50 /* List of the gids of the user */
51 static GTree *current_user_gid = NULL;
53 /* Helper function to compare 2 gids */
54 static gint
55 mc_gid_compare (gconstpointer v, gconstpointer v2)
57 return ((GPOINTER_TO_UINT(v) > GPOINTER_TO_UINT(v2)) ? 1 :
58 (GPOINTER_TO_UINT(v) < GPOINTER_TO_UINT(v2)) ? -1 : 0);
61 /* Helper function to delete keys of the gids tree */
62 static gint
63 mc_gid_destroy (gpointer key, gpointer value, gpointer data)
65 g_free (value);
67 return FALSE;
70 /* This function initialize global GTree with the gids of groups,
71 to which user belongs. Tree also store corresponding string
72 with the name of the group.
73 FIXME: Do we need this names at all? If not, we can simplify
74 initialization by eliminating g_strdup's.
76 void init_groups (void)
78 int i;
79 struct passwd *pwd;
80 struct group *grp;
82 current_user_uid = getuid ();
83 pwd = getpwuid (current_user_uid);
84 g_return_if_fail (pwd != NULL);
86 current_user_gid = g_tree_new (mc_gid_compare);
88 /* Put user's primary group first. */
89 if ((grp = getgrgid (pwd->pw_gid)) != NULL) {
90 g_tree_insert (current_user_gid,
91 GUINT_TO_POINTER ((int) grp->gr_gid),
92 g_strdup (grp->gr_name));
95 setgrent ();
96 while ((grp = getgrent ()) != NULL) {
97 for (i = 0; grp->gr_mem[i]; i++) {
98 if (!strcmp (pwd->pw_name, grp->gr_mem[i]) &&
99 !g_tree_lookup (current_user_gid,
100 GUINT_TO_POINTER ((int) grp->gr_gid))) {
101 g_tree_insert (current_user_gid,
102 GUINT_TO_POINTER ((int) grp->gr_gid),
103 g_strdup (grp->gr_name));
104 break;
108 endgrent ();
111 /* Return the index of the permissions triplet */
113 get_user_permissions (struct stat *buf) {
115 if (buf->st_uid == current_user_uid || current_user_uid == 0)
116 return 0;
118 if (current_user_gid && g_tree_lookup (current_user_gid,
119 GUINT_TO_POINTER((int) buf->st_gid)))
120 return 1;
122 return 2;
125 /* Completely destroys the gids tree */
126 void
127 destroy_groups (void)
129 g_tree_traverse (current_user_gid, mc_gid_destroy, G_POST_ORDER, NULL);
130 g_tree_destroy (current_user_gid);
133 #define UID_CACHE_SIZE 200
134 #define GID_CACHE_SIZE 30
136 typedef struct {
137 int index;
138 char *string;
139 } int_cache;
141 static int_cache uid_cache [UID_CACHE_SIZE];
142 static int_cache gid_cache [GID_CACHE_SIZE];
144 static char *i_cache_match (int id, int_cache *cache, int size)
146 int i;
148 for (i = 0; i < size; i++)
149 if (cache [i].index == id)
150 return cache [i].string;
151 return 0;
154 static void i_cache_add (int id, int_cache *cache, int size, char *text,
155 int *last)
157 if (cache [*last].string)
158 g_free (cache [*last].string);
159 cache [*last].string = g_strdup (text);
160 cache [*last].index = id;
161 *last = ((*last)+1) % size;
164 char *get_owner (int uid)
166 struct passwd *pwd;
167 static char ibuf [10];
168 char *name;
169 static int uid_last;
171 if ((name = i_cache_match (uid, uid_cache, UID_CACHE_SIZE)) != NULL)
172 return name;
174 pwd = getpwuid (uid);
175 if (pwd){
176 i_cache_add (uid, uid_cache, UID_CACHE_SIZE, pwd->pw_name, &uid_last);
177 return pwd->pw_name;
179 else {
180 g_snprintf (ibuf, sizeof (ibuf), "%d", uid);
181 return ibuf;
185 char *get_group (int gid)
187 struct group *grp;
188 static char gbuf [10];
189 char *name;
190 static int gid_last;
192 if ((name = i_cache_match (gid, gid_cache, GID_CACHE_SIZE)) != NULL)
193 return name;
195 grp = getgrgid (gid);
196 if (grp){
197 i_cache_add (gid, gid_cache, GID_CACHE_SIZE, grp->gr_name, &gid_last);
198 return grp->gr_name;
199 } else {
200 g_snprintf (gbuf, sizeof (gbuf), "%d", gid);
201 return gbuf;
205 /* Since ncurses uses a handler that automatically refreshes the */
206 /* screen after a SIGCONT, and we don't want this behavior when */
207 /* spawning a child, we save the original handler here */
208 void save_stop_handler (void)
210 sigaction (SIGTSTP, NULL, &startup_handler);
212 #endif /* VFS_STANDALONE */
214 int my_system (int flags, const char *shell, const char *command)
216 struct sigaction ignore, save_intr, save_quit, save_stop;
217 pid_t pid;
218 int status = 0;
220 ignore.sa_handler = SIG_IGN;
221 sigemptyset (&ignore.sa_mask);
222 ignore.sa_flags = 0;
224 sigaction (SIGINT, &ignore, &save_intr);
225 sigaction (SIGQUIT, &ignore, &save_quit);
227 /* Restore the original SIGTSTP handler, we don't want ncurses' */
228 /* handler messing the screen after the SIGCONT */
229 sigaction (SIGTSTP, &startup_handler, &save_stop);
231 if ((pid = fork ()) < 0){
232 fprintf (stderr, "\n\nfork () = -1\n");
233 return -1;
235 if (pid == 0){
236 signal (SIGINT, SIG_DFL);
237 signal (SIGQUIT, SIG_DFL);
238 signal (SIGTSTP, SIG_DFL);
239 signal (SIGCHLD, SIG_DFL);
241 if (flags & EXECUTE_AS_SHELL)
242 execl (shell, shell, "-c", command, NULL);
243 else
244 execlp (shell, shell, command, NULL);
246 _exit (127); /* Exec error */
247 } else {
248 while (waitpid (pid, &status, 0) < 0)
249 if (errno != EINTR){
250 status = -1;
251 break;
254 sigaction (SIGINT, &save_intr, NULL);
255 sigaction (SIGQUIT, &save_quit, NULL);
256 sigaction (SIGTSTP, &save_stop, NULL);
258 #ifdef SCO_FLAVOR
259 waitpid(-1, NULL, WNOHANG);
260 #endif /* SCO_FLAVOR */
262 return WEXITSTATUS(status);
265 /* Returns a newly allocated string, if directory does not exist, return 0 */
266 char *tilde_expand (const char *directory)
268 struct passwd *passwd;
269 const char *p;
270 char *name;
272 if (*directory != '~')
273 return g_strdup (directory);
275 directory++;
277 p = strchr (directory, PATH_SEP);
279 /* d = "~" or d = "~/" */
280 if (!(*directory) || (*directory == PATH_SEP)){
281 passwd = getpwuid (geteuid ());
282 p = (*directory == PATH_SEP) ? directory+1 : "";
283 } else {
284 if (!p){
285 passwd = getpwnam (directory);
286 } else {
287 name = g_malloc (p - directory + 1);
288 strncpy (name, directory, p - directory);
289 name [p - directory] = 0;
290 passwd = getpwnam (name);
291 g_free (name);
295 /* If we can't figure the user name, return NULL */
296 if (!passwd)
297 return 0;
299 return g_strconcat (passwd->pw_dir, PATH_SEP_STR, p, NULL);
302 #ifndef VFS_STANDALONE
304 /* Pipes are guaranteed to be able to hold at least 4096 bytes */
305 /* More than that would be unportable */
306 #define MAX_PIPE_SIZE 4096
308 static int error_pipe[2]; /* File descriptors of error pipe */
309 static int old_error; /* File descriptor of old standard error */
311 /* Creates a pipe to hold standard error for a later analysis. */
312 /* The pipe can hold 4096 bytes. Make sure no more is written */
313 /* or a deadlock might occur. */
314 void open_error_pipe (void)
316 if (pipe (error_pipe) < 0){
317 message (0, _(" Warning "), _(" Pipe failed "));
319 old_error = dup (2);
320 if(old_error < 0 || close(2) || dup (error_pipe[1]) != 2){
321 message (0, _(" Warning "), _(" Dup failed "));
322 close (error_pipe[0]);
323 close (error_pipe[1]);
325 close (error_pipe[1]);
329 * Returns true if an error was displayed
330 * error: -1 - ignore errors, 0 - display warning, 1 - display error
331 * text is prepended to the error message from the pipe
334 close_error_pipe (int error, char *text)
336 char *title;
337 char msg[MAX_PIPE_SIZE];
338 int len = 0;
340 if (error)
341 title = MSG_ERROR;
342 else
343 title = _(" Warning ");
344 if (old_error >= 0){
345 close (2);
346 dup (old_error);
347 close (old_error);
348 len = read (error_pipe[0], msg, MAX_PIPE_SIZE);
350 if (len >= 0)
351 msg[len] = 0;
352 close (error_pipe[0]);
354 if (error < 0)
355 return 0; /* Just ignore error message */
356 if (text == NULL){
357 if (len <= 0)
358 return 0; /* Nothing to show */
360 /* Show message from pipe */
361 message (error, title, "%s", msg);
362 } else {
363 /* Show given text and possible message from pipe */
364 message (error, title, " %s \n %s ", text, msg);
366 return 1;
369 /* Checks for messages in the error pipe,
370 * closes the pipe and displays an error box if needed
372 void check_error_pipe (void)
374 char error[MAX_PIPE_SIZE];
375 int len = 0;
376 if (old_error >= 0){
377 while (len < MAX_PIPE_SIZE)
379 fd_set select_set;
380 struct timeval timeout;
381 FD_ZERO (&select_set);
382 FD_SET (error_pipe[0], &select_set);
383 timeout.tv_sec = 0;
384 timeout.tv_usec = 0;
385 select (error_pipe[0] + 1, &select_set, 0, 0, &timeout);
386 if (!FD_ISSET (error_pipe[0], &select_set))
387 break;
388 read (error_pipe[0], error + len, 1);
389 len ++;
391 error[len] = 0;
392 close (error_pipe[0]);
394 if (len > 0)
395 message (0, _(" Warning "), "%s", error);
397 #endif /* !VFS_STANDALONE */
399 static struct sigaction ignore, save_intr, save_quit, save_stop;
401 /* INHANDLE is a result of some mc_open call to any vfs, this function
402 returns a normal handle (to be used with read) of a pipe for reading
403 of the output of COMMAND with arguments ... (must include argv[0] as
404 well) which gets as its input at most INLEN bytes from the INHANDLE
405 using mc_read. You have to call mc_doublepclose to close the returned
406 handle afterwards. If INLEN is -1, we read as much as we can :) */
407 int mc_doublepopen (int inhandle, int inlen, pid_t *the_pid, char *command, ...)
409 int pipe0 [2], pipe1 [2];
410 pid_t pid;
412 #define closepipes() close(pipe0[0]);close(pipe0[1]);close(pipe1[0]);close(pipe1[1])
414 pipe (pipe0); pipe (pipe1);
415 ignore.sa_handler = SIG_IGN;
416 sigemptyset (&ignore.sa_mask);
417 ignore.sa_flags = 0;
419 sigaction (SIGINT, &ignore, &save_intr);
420 sigaction (SIGQUIT, &ignore, &save_quit);
421 sigaction (SIGTSTP, &startup_handler, &save_stop);
423 switch (pid = fork ()) {
424 case -1:
425 closepipes ();
426 return -1;
427 case 0: {
428 sigaction (SIGINT, &save_intr, NULL);
429 sigaction (SIGQUIT, &save_quit, NULL);
430 switch (pid = fork ()) {
431 case -1:
432 closepipes ();
433 _exit (1);
434 case 0: {
435 #define MAXARGS 16
436 int argno;
437 char *args[MAXARGS];
438 va_list ap;
439 int nulldevice;
441 nulldevice = open ("/dev/null", O_WRONLY);
442 close (0);
443 dup (pipe0 [0]);
444 close (1);
445 dup (pipe1 [1]);
446 close (2);
447 dup (nulldevice);
448 close (nulldevice);
449 closepipes ();
450 va_start (ap, command);
451 argno = 0;
452 while ((args[argno++] = va_arg(ap, char *)) != NULL)
453 if (argno == (MAXARGS - 1)) {
454 args[argno] = NULL;
455 break;
457 va_end (ap);
458 execvp (command, args);
460 /* If we are here exec has failed */
461 _exit (0);
463 default:
465 char buffer [8192];
466 int i;
468 close (pipe0 [0]);
469 close (pipe1 [0]);
470 close (pipe1 [1]);
471 while ((i = mc_read (inhandle, buffer,
472 (inlen == -1 || inlen > 8192)
473 ? 8192 : inlen)) > 0) {
474 write (pipe0 [1], buffer, i);
475 if (inlen != -1) {
476 inlen -= i;
477 if (!inlen)
478 break;
481 close (inhandle);
482 close (pipe0 [1]);
483 while (waitpid (pid, &i, 0) < 0)
484 if (errno != EINTR)
485 break;
487 _exit (i);
491 default:
492 *the_pid = pid;
493 break;
495 close (pipe0 [0]);
496 close (pipe0 [1]);
497 close (pipe1 [1]);
498 return pipe1 [0];
501 int mc_doublepclose (int pipe, pid_t pid)
503 int status = 0;
505 close (pipe);
506 waitpid (pid, &status, 0);
507 #ifdef SCO_FLAVOR
508 waitpid (-1, NULL, WNOHANG);
509 #endif /* SCO_FLAVOR */
510 sigaction (SIGINT, &save_intr, NULL);
511 sigaction (SIGQUIT, &save_quit, NULL);
512 sigaction (SIGTSTP, &save_stop, NULL);
514 return status;
517 /* Canonicalize path, and return a new path. Do everything in situ.
518 The new path differs from path in:
519 Multiple `/'s are collapsed to a single `/'.
520 Leading `./'s and trailing `/.'s are removed.
521 Trailing `/'s are removed.
522 Non-leading `../'s and trailing `..'s are handled by removing
523 portions of the path. */
524 char *canonicalize_pathname (char *path)
526 int i, start;
527 char stub_char;
529 if (!*path)
530 return path;
532 stub_char = (*path == PATH_SEP) ? PATH_SEP : '.';
534 /* Walk along path looking for things to compact. */
535 i = 0;
536 while (path[i]) {
538 while (path[i] && path[i] != PATH_SEP)
539 i++;
541 start = i++;
543 /* If we didn't find any slashes, then there is nothing left to do. */
544 if (!path[start])
545 break;
547 #if defined(__QNX__) && !defined(__QNXNTO__)
549 ** QNX accesses the directories of nodes on its peer-to-peer
550 ** network (Qnet) by prefixing their directories with "//[nid]".
551 ** We don't want to collapse two '/'s if they're at the start
552 ** of the path, followed by digits, and ending with a '/'.
554 if (start == 0 && path[1] == PATH_SEP) {
555 char *p = path + 2;
556 char *q = strchr (p, PATH_SEP);
558 if (q > p) {
559 *q = 0;
560 if (!strcspn (p, "0123456789")) {
561 start = q - path;
562 i = start + 1;
564 *q = PATH_SEP;
567 #endif
569 /* Handle multiple `/'s in a row. */
570 while (path[i] == PATH_SEP)
571 i++;
573 if ((start + 1) != i) {
574 strcpy (path + start + 1, path + i);
575 i = start + 1;
578 /* Check for trailing `/'. */
579 if (start && !path[i]) {
580 zero_last:
581 path[--i] = '\0';
582 break;
585 /* Check for `../', `./' or trailing `.' by itself. */
586 if (path[i] == '.') {
587 /* Handle trailing `.' by itself. */
588 if (!path[i + 1])
589 goto zero_last;
591 /* Handle `./'. */
592 if (path[i + 1] == PATH_SEP) {
593 strcpy (path + i, path + i + 1);
594 i = start;
595 continue;
598 /* Handle `../' or trailing `..' by itself.
599 Remove the previous ?/ part with the exception of
600 ../, which we should leave intact. */
601 if (path[i + 1] == '.'
602 && (path[i + 2] == PATH_SEP || !path[i + 2])) {
603 while (--start > -1 && path[start] != PATH_SEP);
604 if (!strncmp (path + start + 1, "../", 3))
605 continue;
606 strcpy (path + start + 1, path + i + 2);
607 i = start;
608 continue;
613 if (!*path) {
614 *path = stub_char;
615 path[1] = '\0';
617 return path;
620 #ifndef VFS_STANDALONE
621 #ifdef SCO_FLAVOR
622 int gettimeofday( struct timeval * tv, struct timezone * tz)
624 struct timeb tb;
625 struct tm * l;
627 ftime( &tb );
628 if (errno == EFAULT)
629 return -1;
630 l = localtime(&tb.time);
631 tv->tv_sec = l->tm_sec;
632 tv->tv_usec = (long) tb.millitm;
633 return 0;
635 #endif /* SCO_FLAVOR */
637 #ifdef HAVE_GET_PROCESS_STATS
638 # include <sys/procstats.h>
640 int gettimeofday (struct timeval *tp, void *tzp)
642 return get_process_stats(tp, PS_SELF, 0, 0);
644 #endif /* HAVE_GET_PROCESS_STATS */
646 #ifndef HAVE_PUTENV
648 /* The following piece of code was copied from the GNU C Library */
649 /* And is provided here for nextstep who lacks putenv */
651 extern char **environ;
653 #ifndef HAVE_GNU_LD
654 #define __environ environ
655 #endif
658 /* Put STRING, which is of the form "NAME=VALUE", in the environment. */
660 putenv (const char *string)
662 const char *const name_end = strchr (string, '=');
663 register size_t size;
664 register char **ep;
666 if (name_end == NULL){
667 /* Remove the variable from the environment. */
668 size = strlen (string);
669 for (ep = __environ; *ep != NULL; ++ep)
670 if (!strncmp (*ep, string, size) && (*ep)[size] == '='){
671 while (ep[1] != NULL){
672 ep[0] = ep[1];
673 ++ep;
675 *ep = NULL;
676 return 0;
680 size = 0;
681 for (ep = __environ; *ep != NULL; ++ep)
682 if (!strncmp (*ep, string, name_end - string) &&
683 (*ep)[name_end - string] == '=')
684 break;
685 else
686 ++size;
688 if (*ep == NULL){
689 static char **last_environ = NULL;
690 char **new_environ = g_new (char *, size + 2);
691 if (new_environ == NULL)
692 return -1;
693 (void) memcpy ((void *) new_environ, (void *) __environ,
694 size * sizeof (char *));
695 new_environ[size] = (char *) string;
696 new_environ[size + 1] = NULL;
697 if (last_environ != NULL)
698 g_free ((void *) last_environ);
699 last_environ = new_environ;
700 __environ = new_environ;
702 else
703 *ep = (char *) string;
705 return 0;
707 #endif /* !HAVE_PUTENV */
709 #ifdef SCO_FLAVOR
710 /* Define this only for SCO */
711 #ifdef USE_NETCODE
712 #ifndef HAVE_SOCKETPAIR
715 The code for s_pipe function is adapted from Section 7.9
716 of the "UNIX Network Programming" by W. Richard Stevens,
717 published by Prentice Hall, ISBN 0-13-949876-1
718 (c) 1990 by P T R Prentice Hall
720 It is used to implement socketpair function for SVR3 systems
721 that lack it.
724 #include <sys/types.h>
725 #include <sys/stream.h> /* defines queue_t */
726 #include <stropts.h> /* defines struct strtdinsert */
727 #include <fcntl.h>
729 #define SPX_DEVICE "/dev/spx"
730 #define S_PIPE_HANDLE_ERRNO 1
731 /* if the above is defined to 1, we will attempt to
732 save and restore errno to indicate failure
733 reason to the caller;
734 Please note that this will not work in environments
735 where errno is not just an integer
738 #if S_PIPE_HANDLE_ERRNO
739 #include <errno.h>
740 /* This is for "extern int errno;" */
741 #endif
743 /* s_pipe returns 0 if OK, -1 on error */
744 /* two file descriptors are returned */
745 static int s_pipe(int fd[2])
747 struct strfdinsert ins; /* stream I_FDINSERT ioctl format */
748 queue_t *pointer;
749 #if S_PIPE_HANDLE_ERRNO
750 int err_save;
751 #endif
753 * First open the stream clone device "dev/spx" twice,
754 * obtaining the two file descriptors
757 if ( (fd[0] = open(SPX_DEVICE, O_RDWR)) < 0)
758 return -1;
759 if ( (fd[1] = open(SPX_DEVICE, O_RDWR)) < 0) {
760 #if S_PIPE_HANDLE_ERRNO
761 err_save = errno;
762 #endif
763 close(fd[0]);
764 #if S_PIPE_HANDLE_ERRNO
765 errno = err_save;
766 #endif
767 return -1;
771 * Now link these two stream together with an I_FDINSERT ioctl.
774 ins.ctlbuf.buf = (char *) &pointer; /* no control information, just the pointer */
775 ins.ctlbuf.maxlen = sizeof pointer;
776 ins.ctlbuf.len = sizeof pointer;
777 ins.databuf.buf = (char *) 0; /* no data to be sent */
778 ins.databuf.maxlen = 0;
779 ins.databuf.len = -1; /* magic: must be -1 rather than 0 for stream pipes */
781 ins.fildes = fd[1]; /* the fd to connect with fd[0] */
782 ins.flags = 0; /* nonpriority message */
783 ins.offset = 0; /* offset of pointer in control buffer */
785 if (ioctl(fd[0], I_FDINSERT, (char *) &ins) < 0) {
786 #if S_PIPE_HANDLE_ERRNO
787 err_save = errno;
788 #endif
789 close(fd[0]);
790 close(fd[1]);
791 #if S_PIPE_HANDLE_ERRNO
792 errno = err_save;
793 #endif
794 return -1;
796 /* all is OK if we came here, indicate success */
797 return 0;
800 int socketpair(int dummy1, int dummy2, int dummy3, int fd[2])
802 return s_pipe(fd);
805 #endif /* ifndef HAVE_SOCKETPAIR */
806 #endif /* ifdef USE_NETCODE */
807 #endif /* SCO_FLAVOR */
808 #endif /* VFS_STANDALONE */