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. */
23 #include <sys/types.h>
28 #include <signal.h> /* my_system */
29 #include <limits.h> /* INT_MAX */
32 #include <errno.h> /* my_system */
35 #ifdef HAVE_SYS_IOCTL_H
36 # include <sys/ioctl.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 */
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 */
63 mc_gid_destroy (gpointer key
, gpointer value
, gpointer data
)
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)
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
));
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
));
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)
118 if (current_user_gid
&& g_tree_lookup (current_user_gid
,
119 GUINT_TO_POINTER((int) buf
->st_gid
)))
125 /* Completely destroys the gids tree */
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
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
)
148 for (i
= 0; i
< size
; i
++)
149 if (cache
[i
].index
== id
)
150 return cache
[i
].string
;
154 static void i_cache_add (int id
, int_cache
*cache
, int size
, char *text
,
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
)
167 static char ibuf
[10];
171 if ((name
= i_cache_match (uid
, uid_cache
, UID_CACHE_SIZE
)) != NULL
)
174 pwd
= getpwuid (uid
);
176 i_cache_add (uid
, uid_cache
, UID_CACHE_SIZE
, pwd
->pw_name
, &uid_last
);
180 g_snprintf (ibuf
, sizeof (ibuf
), "%d", uid
);
185 char *get_group (int gid
)
188 static char gbuf
[10];
192 if ((name
= i_cache_match (gid
, gid_cache
, GID_CACHE_SIZE
)) != NULL
)
195 grp
= getgrgid (gid
);
197 i_cache_add (gid
, gid_cache
, GID_CACHE_SIZE
, grp
->gr_name
, &gid_last
);
200 g_snprintf (gbuf
, sizeof (gbuf
), "%d", gid
);
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
;
220 ignore
.sa_handler
= SIG_IGN
;
221 sigemptyset (&ignore
.sa_mask
);
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");
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
);
244 execlp (shell
, shell
, command
, NULL
);
246 _exit (127); /* Exec error */
248 while (waitpid (pid
, &status
, 0) < 0)
254 sigaction (SIGINT
, &save_intr
, NULL
);
255 sigaction (SIGQUIT
, &save_quit
, NULL
);
256 sigaction (SIGTSTP
, &save_stop
, NULL
);
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
;
272 if (*directory
!= '~')
273 return g_strdup (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 : "";
285 passwd
= getpwnam (directory
);
287 name
= g_malloc (p
- directory
+ 1);
288 strncpy (name
, directory
, p
- directory
);
289 name
[p
- directory
] = 0;
290 passwd
= getpwnam (name
);
295 /* If we can't figure the user name, return NULL */
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 "));
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
332 close_error_pipe (int error
, char *text
)
335 char msg
[MAX_PIPE_SIZE
];
341 title
= _(" Warning ");
346 len
= read (error_pipe
[0], msg
, MAX_PIPE_SIZE
);
350 close (error_pipe
[0]);
353 return 0; /* Just ignore error message */
355 if (len
== 0) return 0; /* Nothing to show */
357 /* Show message from pipe */
358 message (error
, title
, "%s", msg
);
360 /* Show given text and possible message from pipe */
361 message (error
, title
, " %s \n %s ", text
, msg
);
366 /* Checks for messages in the error pipe,
367 * closes the pipe and displays an error box if needed
369 void check_error_pipe (void)
371 char error
[MAX_PIPE_SIZE
];
374 while (len
< MAX_PIPE_SIZE
)
377 struct timeval timeout
;
378 FD_ZERO (&select_set
);
379 FD_SET (error_pipe
[0], &select_set
);
382 select (FD_SETSIZE
, &select_set
, 0, 0, &timeout
);
383 if (!FD_ISSET (0, &select_set
))
385 read (error_pipe
[0], error
+ len
, 1);
389 close (error_pipe
[0]);
392 message (0, _(" Warning "), "%s", error
);
394 #endif /* !VFS_STANDALONE */
396 static struct sigaction ignore
, save_intr
, save_quit
, save_stop
;
398 /* INHANDLE is a result of some mc_open call to any vfs, this function
399 returns a normal handle (to be used with read) of a pipe for reading
400 of the output of COMMAND with arguments ... (must include argv[0] as
401 well) which gets as its input at most INLEN bytes from the INHANDLE
402 using mc_read. You have to call mc_doublepclose to close the returned
403 handle afterwards. If INLEN is -1, we read as much as we can :) */
404 int mc_doublepopen (int inhandle
, int inlen
, pid_t
*the_pid
, char *command
, ...)
406 int pipe0
[2], pipe1
[2];
409 #define closepipes() close(pipe0[0]);close(pipe0[1]);close(pipe1[0]);close(pipe1[1])
411 pipe (pipe0
); pipe (pipe1
);
412 ignore
.sa_handler
= SIG_IGN
;
413 sigemptyset (&ignore
.sa_mask
);
416 sigaction (SIGINT
, &ignore
, &save_intr
);
417 sigaction (SIGQUIT
, &ignore
, &save_quit
);
418 sigaction (SIGTSTP
, &startup_handler
, &save_stop
);
420 switch (pid
= fork ()) {
425 sigaction (SIGINT
, &save_intr
, NULL
);
426 sigaction (SIGQUIT
, &save_quit
, NULL
);
427 switch (pid
= fork ()) {
438 nulldevice
= open ("/dev/null", O_WRONLY
);
447 va_start (ap
, command
);
449 while ((args
[argno
++] = va_arg(ap
, char *)) != NULL
)
450 if (argno
== (MAXARGS
- 1)) {
455 execvp (command
, args
);
457 /* If we are here exec has failed */
468 while ((i
= mc_read (inhandle
, buffer
,
469 (inlen
== -1 || inlen
> 8192)
470 ? 8192 : inlen
)) > 0) {
471 write (pipe0
[1], buffer
, i
);
480 while (waitpid (pid
, &i
, 0) < 0)
498 int mc_doublepclose (int pipe
, pid_t pid
)
503 waitpid (pid
, &status
, 0);
505 waitpid (-1, NULL
, WNOHANG
);
506 #endif /* SCO_FLAVOR */
507 sigaction (SIGINT
, &save_intr
, NULL
);
508 sigaction (SIGQUIT
, &save_quit
, NULL
);
509 sigaction (SIGTSTP
, &save_stop
, NULL
);
514 /* Canonicalize path, and return a new path. Do everything in situ.
515 The new path differs from path in:
516 Multiple `/'s are collapsed to a single `/'.
517 Leading `./'s and trailing `/.'s are removed.
518 Trailing `/'s are removed.
519 Non-leading `../'s and trailing `..'s are handled by removing
520 portions of the path. */
521 char *canonicalize_pathname (char *path
)
529 stub_char
= (*path
== PATH_SEP
) ? PATH_SEP
: '.';
531 /* Walk along path looking for things to compact. */
535 while (path
[i
] && path
[i
] != PATH_SEP
)
540 /* If we didn't find any slashes, then there is nothing left to do. */
544 #if defined(__QNX__) && !defined(__QNXNTO__)
546 ** QNX accesses the directories of nodes on its peer-to-peer
547 ** network (Qnet) by prefixing their directories with "//[nid]".
548 ** We don't want to collapse two '/'s if they're at the start
549 ** of the path, followed by digits, and ending with a '/'.
551 if (start
== 0 && path
[1] == PATH_SEP
) {
553 char *q
= strchr (p
, PATH_SEP
);
557 if (!strcspn (p
, "0123456789")) {
566 /* Handle multiple `/'s in a row. */
567 while (path
[i
] == PATH_SEP
)
570 if ((start
+ 1) != i
) {
571 strcpy (path
+ start
+ 1, path
+ i
);
575 /* Check for trailing `/'. */
576 if (start
&& !path
[i
]) {
582 /* Check for `../', `./' or trailing `.' by itself. */
583 if (path
[i
] == '.') {
584 /* Handle trailing `.' by itself. */
589 if (path
[i
+ 1] == PATH_SEP
) {
590 strcpy (path
+ i
, path
+ i
+ 1);
595 /* Handle `../' or trailing `..' by itself.
596 Remove the previous ?/ part with the exception of
597 ../, which we should leave intact. */
598 if (path
[i
+ 1] == '.'
599 && (path
[i
+ 2] == PATH_SEP
|| !path
[i
+ 2])) {
600 while (--start
> -1 && path
[start
] != PATH_SEP
);
601 if (!strncmp (path
+ start
+ 1, "../", 3))
603 strcpy (path
+ start
+ 1, path
+ i
+ 2);
617 #ifndef VFS_STANDALONE
619 int gettimeofday( struct timeval
* tv
, struct timezone
* tz
)
627 l
= localtime(&tb
.time
);
628 tv
->tv_sec
= l
->tm_sec
;
629 tv
->tv_usec
= (long) tb
.millitm
;
632 #endif /* SCO_FLAVOR */
634 #ifdef HAVE_GET_PROCESS_STATS
635 # include <sys/procstats.h>
637 int gettimeofday (struct timeval
*tp
, void *tzp
)
639 return get_process_stats(tp
, PS_SELF
, 0, 0);
641 #endif /* HAVE_GET_PROCESS_STATS */
645 /* The following piece of code was copied from the GNU C Library */
646 /* And is provided here for nextstep who lacks putenv */
648 extern char **environ
;
651 #define __environ environ
655 /* Put STRING, which is of the form "NAME=VALUE", in the environment. */
657 putenv (const char *string
)
659 const char *const name_end
= strchr (string
, '=');
660 register size_t size
;
663 if (name_end
== NULL
){
664 /* Remove the variable from the environment. */
665 size
= strlen (string
);
666 for (ep
= __environ
; *ep
!= NULL
; ++ep
)
667 if (!strncmp (*ep
, string
, size
) && (*ep
)[size
] == '='){
668 while (ep
[1] != NULL
){
678 for (ep
= __environ
; *ep
!= NULL
; ++ep
)
679 if (!strncmp (*ep
, string
, name_end
- string
) &&
680 (*ep
)[name_end
- string
] == '=')
686 static char **last_environ
= NULL
;
687 char **new_environ
= g_new (char *, size
+ 2);
688 if (new_environ
== NULL
)
690 (void) memcpy ((void *) new_environ
, (void *) __environ
,
691 size
* sizeof (char *));
692 new_environ
[size
] = (char *) string
;
693 new_environ
[size
+ 1] = NULL
;
694 if (last_environ
!= NULL
)
695 g_free ((void *) last_environ
);
696 last_environ
= new_environ
;
697 __environ
= new_environ
;
700 *ep
= (char *) string
;
704 #endif /* !HAVE_PUTENV */
707 /* Define this only for SCO */
709 #ifndef HAVE_SOCKETPAIR
712 The code for s_pipe function is adapted from Section 7.9
713 of the "UNIX Network Programming" by W. Richard Stevens,
714 published by Prentice Hall, ISBN 0-13-949876-1
715 (c) 1990 by P T R Prentice Hall
717 It is used to implement socketpair function for SVR3 systems
721 #include <sys/types.h>
722 #include <sys/stream.h> /* defines queue_t */
723 #include <stropts.h> /* defines struct strtdinsert */
726 #define SPX_DEVICE "/dev/spx"
727 #define S_PIPE_HANDLE_ERRNO 1
728 /* if the above is defined to 1, we will attempt to
729 save and restore errno to indicate failure
730 reason to the caller;
731 Please note that this will not work in environments
732 where errno is not just an integer
735 #if S_PIPE_HANDLE_ERRNO
737 /* This is for "extern int errno;" */
740 /* s_pipe returns 0 if OK, -1 on error */
741 /* two file descriptors are returned */
742 static int s_pipe(int fd
[2])
744 struct strfdinsert ins
; /* stream I_FDINSERT ioctl format */
746 #if S_PIPE_HANDLE_ERRNO
750 * First open the stream clone device "dev/spx" twice,
751 * obtaining the two file descriptors
754 if ( (fd
[0] = open(SPX_DEVICE
, O_RDWR
)) < 0)
756 if ( (fd
[1] = open(SPX_DEVICE
, O_RDWR
)) < 0) {
757 #if S_PIPE_HANDLE_ERRNO
761 #if S_PIPE_HANDLE_ERRNO
768 * Now link these two stream together with an I_FDINSERT ioctl.
771 ins
.ctlbuf
.buf
= (char *) &pointer
; /* no control information, just the pointer */
772 ins
.ctlbuf
.maxlen
= sizeof pointer
;
773 ins
.ctlbuf
.len
= sizeof pointer
;
774 ins
.databuf
.buf
= (char *) 0; /* no data to be sent */
775 ins
.databuf
.maxlen
= 0;
776 ins
.databuf
.len
= -1; /* magic: must be -1 rather than 0 for stream pipes */
778 ins
.fildes
= fd
[1]; /* the fd to connect with fd[0] */
779 ins
.flags
= 0; /* nonpriority message */
780 ins
.offset
= 0; /* offset of pointer in control buffer */
782 if (ioctl(fd
[0], I_FDINSERT
, (char *) &ins
) < 0) {
783 #if S_PIPE_HANDLE_ERRNO
788 #if S_PIPE_HANDLE_ERRNO
793 /* all is OK if we came here, indicate success */
797 int socketpair(int dummy1
, int dummy2
, int dummy3
, int fd
[2])
802 #endif /* ifndef HAVE_SOCKETPAIR */
803 #endif /* ifdef USE_NETCODE */
804 #endif /* SCO_FLAVOR */
805 #endif /* VFS_STANDALONE */