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>
27 #include <signal.h> /* struct sigaction */
28 #include <limits.h> /* INT_MAX */
31 #include <errno.h> /* errno */
34 #ifdef HAVE_SYS_IOCTL_H
35 # include <sys/ioctl.h>
40 #include "mountlist.h"
41 #include "wtools.h" /* message() */
43 struct sigaction startup_handler
;
45 /* uid of the MC user */
46 static uid_t current_user_uid
= -1;
47 /* List of the gids of the user */
48 static GTree
*current_user_gid
= NULL
;
50 /* Helper function to compare 2 gids */
52 mc_gid_compare (gconstpointer v
, gconstpointer v2
)
54 return ((GPOINTER_TO_UINT(v
) > GPOINTER_TO_UINT(v2
)) ? 1 :
55 (GPOINTER_TO_UINT(v
) < GPOINTER_TO_UINT(v2
)) ? -1 : 0);
58 /* Helper function to delete keys of the gids tree */
60 mc_gid_destroy (gpointer key
, gpointer value
, gpointer data
)
67 /* This function initialize global GTree with the gids of groups,
68 to which user belongs. Tree also store corresponding string
69 with the name of the group.
70 FIXME: Do we need this names at all? If not, we can simplify
71 initialization by eliminating g_strdup's.
73 void init_groups (void)
79 current_user_uid
= getuid ();
80 pwd
= getpwuid (current_user_uid
);
81 g_return_if_fail (pwd
!= NULL
);
83 current_user_gid
= g_tree_new (mc_gid_compare
);
85 /* Put user's primary group first. */
86 if ((grp
= getgrgid (pwd
->pw_gid
)) != NULL
) {
87 g_tree_insert (current_user_gid
,
88 GUINT_TO_POINTER ((int) grp
->gr_gid
),
89 g_strdup (grp
->gr_name
));
93 while ((grp
= getgrent ()) != NULL
) {
94 for (i
= 0; grp
->gr_mem
[i
]; i
++) {
95 if (!strcmp (pwd
->pw_name
, grp
->gr_mem
[i
]) &&
96 !g_tree_lookup (current_user_gid
,
97 GUINT_TO_POINTER ((int) grp
->gr_gid
))) {
98 g_tree_insert (current_user_gid
,
99 GUINT_TO_POINTER ((int) grp
->gr_gid
),
100 g_strdup (grp
->gr_name
));
108 /* Return the index of the permissions triplet */
110 get_user_permissions (struct stat
*buf
) {
112 if (buf
->st_uid
== current_user_uid
|| current_user_uid
== 0)
115 if (current_user_gid
&& g_tree_lookup (current_user_gid
,
116 GUINT_TO_POINTER((int) buf
->st_gid
)))
122 /* Completely destroys the gids tree */
124 destroy_groups (void)
126 g_tree_traverse (current_user_gid
, mc_gid_destroy
, G_POST_ORDER
, NULL
);
127 g_tree_destroy (current_user_gid
);
130 #define UID_CACHE_SIZE 200
131 #define GID_CACHE_SIZE 30
138 static int_cache uid_cache
[UID_CACHE_SIZE
];
139 static int_cache gid_cache
[GID_CACHE_SIZE
];
141 static char *i_cache_match (int id
, int_cache
*cache
, int size
)
145 for (i
= 0; i
< size
; i
++)
146 if (cache
[i
].index
== id
)
147 return cache
[i
].string
;
151 static void i_cache_add (int id
, int_cache
*cache
, int size
, char *text
,
154 if (cache
[*last
].string
)
155 g_free (cache
[*last
].string
);
156 cache
[*last
].string
= g_strdup (text
);
157 cache
[*last
].index
= id
;
158 *last
= ((*last
)+1) % size
;
161 char *get_owner (int uid
)
164 static char ibuf
[10];
168 if ((name
= i_cache_match (uid
, uid_cache
, UID_CACHE_SIZE
)) != NULL
)
171 pwd
= getpwuid (uid
);
173 i_cache_add (uid
, uid_cache
, UID_CACHE_SIZE
, pwd
->pw_name
, &uid_last
);
177 g_snprintf (ibuf
, sizeof (ibuf
), "%d", uid
);
182 char *get_group (int gid
)
185 static char gbuf
[10];
189 if ((name
= i_cache_match (gid
, gid_cache
, GID_CACHE_SIZE
)) != NULL
)
192 grp
= getgrgid (gid
);
194 i_cache_add (gid
, gid_cache
, GID_CACHE_SIZE
, grp
->gr_name
, &gid_last
);
197 g_snprintf (gbuf
, sizeof (gbuf
), "%d", gid
);
202 /* Since ncurses uses a handler that automatically refreshes the */
203 /* screen after a SIGCONT, and we don't want this behavior when */
204 /* spawning a child, we save the original handler here */
205 void save_stop_handler (void)
207 sigaction (SIGTSTP
, NULL
, &startup_handler
);
210 int my_system (int flags
, const char *shell
, const char *command
)
212 struct sigaction ignore
, save_intr
, save_quit
, save_stop
;
216 ignore
.sa_handler
= SIG_IGN
;
217 sigemptyset (&ignore
.sa_mask
);
220 sigaction (SIGINT
, &ignore
, &save_intr
);
221 sigaction (SIGQUIT
, &ignore
, &save_quit
);
223 /* Restore the original SIGTSTP handler, we don't want ncurses' */
224 /* handler messing the screen after the SIGCONT */
225 sigaction (SIGTSTP
, &startup_handler
, &save_stop
);
227 if ((pid
= fork ()) < 0){
228 fprintf (stderr
, "\n\nfork () = -1\n");
232 signal (SIGINT
, SIG_DFL
);
233 signal (SIGQUIT
, SIG_DFL
);
234 signal (SIGTSTP
, SIG_DFL
);
235 signal (SIGCHLD
, SIG_DFL
);
237 if (flags
& EXECUTE_AS_SHELL
)
238 execl (shell
, shell
, "-c", command
, NULL
);
240 execlp (shell
, shell
, command
, NULL
);
242 _exit (127); /* Exec error */
244 while (waitpid (pid
, &status
, 0) < 0)
250 sigaction (SIGINT
, &save_intr
, NULL
);
251 sigaction (SIGQUIT
, &save_quit
, NULL
);
252 sigaction (SIGTSTP
, &save_stop
, NULL
);
255 waitpid(-1, NULL
, WNOHANG
);
256 #endif /* SCO_FLAVOR */
258 return WEXITSTATUS(status
);
261 /* Returns a newly allocated string, if directory does not exist, return 0 */
262 char *tilde_expand (const char *directory
)
264 struct passwd
*passwd
;
268 if (*directory
!= '~')
269 return g_strdup (directory
);
273 p
= strchr (directory
, PATH_SEP
);
275 /* d = "~" or d = "~/" */
276 if (!(*directory
) || (*directory
== PATH_SEP
)){
277 passwd
= getpwuid (geteuid ());
278 p
= (*directory
== PATH_SEP
) ? directory
+1 : "";
281 passwd
= getpwnam (directory
);
283 name
= g_malloc (p
- directory
+ 1);
284 strncpy (name
, directory
, p
- directory
);
285 name
[p
- directory
] = 0;
286 passwd
= getpwnam (name
);
291 /* If we can't figure the user name, return NULL */
295 return g_strconcat (passwd
->pw_dir
, PATH_SEP_STR
, p
, NULL
);
300 * Return the directory where mc should keep its temporary files.
301 * This directory is (in Bourne shell terms) "${TMPDIR=/tmp}-$USER"
302 * When called the first time, the directory is created if needed.
303 * The first call should be done early, since we are using fprintf()
304 * and not message() to report possible problems.
309 static char buffer
[64];
310 static const char *tmpdir
;
314 const char *error
= NULL
;
316 /* Check if already initialized */
320 sys_tmp
= getenv ("TMPDIR");
322 sys_tmp
= TMPDIR_DEFAULT
;
325 pwd
= getpwuid (getuid ());
326 g_snprintf (buffer
, sizeof (buffer
), "%s/mc-%s", sys_tmp
,
328 canonicalize_pathname (buffer
);
330 if (lstat (buffer
, &st
) == 0) {
331 /* Sanity check for existing directory */
332 if (!S_ISDIR (st
.st_mode
))
333 error
= _("%s is not a directory\n");
334 else if (st
.st_uid
!= getuid ())
335 error
= _("Directory %s is not owned by you\n");
336 else if (((st
.st_mode
& 0777) != 0700)
337 && (chmod (buffer
, 0700) != 0))
338 error
= _("Cannot set correct permissions for directory %s\n");
340 /* Need to create directory */
341 if (mkdir (buffer
, S_IRWXU
) != 0) {
343 _("Cannot create temporary directory %s: %s\n"),
344 buffer
, unix_error_string (errno
));
357 fprintf (stderr
, error
, buffer
);
359 /* Test if sys_tmp is suitable for temporary files */
361 test_fd
= mc_mkstemps (&test_fn
, "mctest", NULL
);
364 test_fd
= open (test_fn
, O_RDONLY
);
373 fprintf (stderr
, _("Temporary files will be created in %s\n"),
376 fprintf (stderr
, _("Temporary files will not be created\n"));
377 tmpdir
= "/dev/null/";
380 fprintf (stderr
, "%s\n", _("Press any key to continue..."));
388 /* Pipes are guaranteed to be able to hold at least 4096 bytes */
389 /* More than that would be unportable */
390 #define MAX_PIPE_SIZE 4096
392 static int error_pipe
[2]; /* File descriptors of error pipe */
393 static int old_error
; /* File descriptor of old standard error */
395 /* Creates a pipe to hold standard error for a later analysis. */
396 /* The pipe can hold 4096 bytes. Make sure no more is written */
397 /* or a deadlock might occur. */
398 void open_error_pipe (void)
400 if (pipe (error_pipe
) < 0){
401 message (0, _("Warning"), _(" Pipe failed "));
404 if(old_error
< 0 || close(2) || dup (error_pipe
[1]) != 2){
405 message (0, _("Warning"), _(" Dup failed "));
406 close (error_pipe
[0]);
407 close (error_pipe
[1]);
409 close (error_pipe
[1]);
413 * Returns true if an error was displayed
414 * error: -1 - ignore errors, 0 - display warning, 1 - display error
415 * text is prepended to the error message from the pipe
418 close_error_pipe (int error
, char *text
)
421 char msg
[MAX_PIPE_SIZE
];
427 title
= _("Warning");
432 len
= read (error_pipe
[0], msg
, MAX_PIPE_SIZE
);
436 close (error_pipe
[0]);
439 return 0; /* Just ignore error message */
442 return 0; /* Nothing to show */
444 /* Show message from pipe */
445 message (error
, title
, "%s", msg
);
447 /* Show given text and possible message from pipe */
448 message (error
, title
, " %s \n %s ", text
, msg
);
453 /* Checks for messages in the error pipe,
454 * closes the pipe and displays an error box if needed
456 void check_error_pipe (void)
458 char error
[MAX_PIPE_SIZE
];
461 while (len
< MAX_PIPE_SIZE
)
464 struct timeval timeout
;
465 FD_ZERO (&select_set
);
466 FD_SET (error_pipe
[0], &select_set
);
469 select (error_pipe
[0] + 1, &select_set
, 0, 0, &timeout
);
470 if (!FD_ISSET (error_pipe
[0], &select_set
))
472 read (error_pipe
[0], error
+ len
, 1);
476 close (error_pipe
[0]);
479 message (0, _("Warning"), "%s", error
);
482 static struct sigaction ignore
, save_intr
, save_quit
, save_stop
;
484 /* INHANDLE is a result of some mc_open call to any vfs, this function
485 returns a normal handle (to be used with read) of a pipe for reading
486 of the output of COMMAND with arguments ... (must include argv[0] as
487 well) which gets as its input at most INLEN bytes from the INHANDLE
488 using mc_read. You have to call mc_doublepclose to close the returned
489 handle afterwards. If INLEN is -1, we read as much as we can :) */
490 int mc_doublepopen (int inhandle
, int inlen
, pid_t
*the_pid
, char *command
, ...)
492 int pipe0
[2], pipe1
[2];
495 #define closepipes() close(pipe0[0]);close(pipe0[1]);close(pipe1[0]);close(pipe1[1])
497 pipe (pipe0
); pipe (pipe1
);
498 ignore
.sa_handler
= SIG_IGN
;
499 sigemptyset (&ignore
.sa_mask
);
502 sigaction (SIGINT
, &ignore
, &save_intr
);
503 sigaction (SIGQUIT
, &ignore
, &save_quit
);
504 sigaction (SIGTSTP
, &startup_handler
, &save_stop
);
506 switch (pid
= fork ()) {
511 sigaction (SIGINT
, &save_intr
, NULL
);
512 sigaction (SIGQUIT
, &save_quit
, NULL
);
513 switch (pid
= fork ()) {
524 nulldevice
= open ("/dev/null", O_WRONLY
);
533 va_start (ap
, command
);
535 while ((args
[argno
++] = va_arg(ap
, char *)) != NULL
)
536 if (argno
== (MAXARGS
- 1)) {
541 execvp (command
, args
);
543 /* If we are here exec has failed */
554 while ((i
= mc_read (inhandle
, buffer
,
555 (inlen
== -1 || inlen
> 8192)
556 ? 8192 : inlen
)) > 0) {
557 write (pipe0
[1], buffer
, i
);
566 while (waitpid (pid
, &i
, 0) < 0)
584 int mc_doublepclose (int pipe
, pid_t pid
)
589 waitpid (pid
, &status
, 0);
591 waitpid (-1, NULL
, WNOHANG
);
592 #endif /* SCO_FLAVOR */
593 sigaction (SIGINT
, &save_intr
, NULL
);
594 sigaction (SIGQUIT
, &save_quit
, NULL
);
595 sigaction (SIGTSTP
, &save_stop
, NULL
);
600 /* Canonicalize path, and return a new path. Do everything in situ.
601 The new path differs from path in:
602 Multiple `/'s are collapsed to a single `/'.
603 Leading `./'s and trailing `/.'s are removed.
604 Trailing `/'s are removed.
605 Non-leading `../'s and trailing `..'s are handled by removing
606 portions of the path. */
608 canonicalize_pathname (char *path
)
613 if (!path
[0] || !path
[1])
616 /* Collapse multiple slashes */
619 if (p
[0] == PATH_SEP
&& p
[1] == PATH_SEP
) {
621 while (*(++s
) == PATH_SEP
);
627 /* Collapse "/./" -> "/" */
630 if (p
[0] == PATH_SEP
&& p
[1] == '.' && p
[2] == PATH_SEP
)
636 /* Remove trailing slashes */
637 p
= path
+ strlen (path
) - 1;
638 while (p
> path
&& *p
== PATH_SEP
)
641 /* Remove leading "./" */
642 if (path
[0] == '.' && path
[1] == PATH_SEP
) {
647 strcpy (path
, path
+ 2);
651 /* Remove trailing "/" or "/." */
655 if (path
[len
- 1] == PATH_SEP
) {
658 if (path
[len
- 1] == '.' && path
[len
- 2] == PATH_SEP
) {
668 /* Collapse "/.." with the previous part of path */
670 while (p
[0] && p
[1] && p
[2]) {
671 if ((p
[0] != PATH_SEP
|| p
[1] != '.' || p
[2] != '.')
672 || (p
[3] != PATH_SEP
&& p
[3] != 0)) {
677 /* search for the previous token */
679 while (s
>= path
&& *s
!= PATH_SEP
)
684 /* If the previous token is "..", we cannot collapse it */
685 if (s
[0] == '.' && s
[1] == '.' && s
+ 2 == p
) {
691 if (s
== path
&& *s
== PATH_SEP
) {
692 /* "/../foo" -> "/foo" */
693 strcpy (s
+ 1, p
+ 4);
695 /* "token/../foo" -> "foo" */
698 p
= (s
> path
) ? s
- 1 : s
;
704 /* "token/.." -> "." */
705 if (path
[0] != PATH_SEP
) {
710 /* "foo/token/.." -> "foo" */
725 int gettimeofday( struct timeval
* tv
, struct timezone
* tz
)
733 l
= localtime(&tb
.time
);
734 tv
->tv_sec
= l
->tm_sec
;
735 tv
->tv_usec
= (long) tb
.millitm
;
738 #endif /* SCO_FLAVOR */
740 #ifdef HAVE_GET_PROCESS_STATS
741 # include <sys/procstats.h>
743 int gettimeofday (struct timeval
*tp
, void *tzp
)
745 return get_process_stats(tp
, PS_SELF
, 0, 0);
747 #endif /* HAVE_GET_PROCESS_STATS */
751 /* The following piece of code was copied from the GNU C Library */
752 /* And is provided here for nextstep who lacks putenv */
754 extern char **environ
;
757 #define __environ environ
761 /* Put STRING, which is of the form "NAME=VALUE", in the environment. */
763 putenv (const char *string
)
765 const char *const name_end
= strchr (string
, '=');
766 register size_t size
;
769 if (name_end
== NULL
){
770 /* Remove the variable from the environment. */
771 size
= strlen (string
);
772 for (ep
= __environ
; *ep
!= NULL
; ++ep
)
773 if (!strncmp (*ep
, string
, size
) && (*ep
)[size
] == '='){
774 while (ep
[1] != NULL
){
784 for (ep
= __environ
; *ep
!= NULL
; ++ep
)
785 if (!strncmp (*ep
, string
, name_end
- string
) &&
786 (*ep
)[name_end
- string
] == '=')
792 static char **last_environ
= NULL
;
793 char **new_environ
= g_new (char *, size
+ 2);
794 if (new_environ
== NULL
)
796 (void) memcpy ((void *) new_environ
, (void *) __environ
,
797 size
* sizeof (char *));
798 new_environ
[size
] = (char *) string
;
799 new_environ
[size
+ 1] = NULL
;
800 if (last_environ
!= NULL
)
801 g_free ((void *) last_environ
);
802 last_environ
= new_environ
;
803 __environ
= new_environ
;
806 *ep
= (char *) string
;
810 #endif /* !HAVE_PUTENV */
813 /* Define this only for SCO */
815 #ifndef HAVE_SOCKETPAIR
818 The code for s_pipe function is adapted from Section 7.9
819 of the "UNIX Network Programming" by W. Richard Stevens,
820 published by Prentice Hall, ISBN 0-13-949876-1
821 (c) 1990 by P T R Prentice Hall
823 It is used to implement socketpair function for SVR3 systems
827 #include <sys/types.h>
828 #include <sys/stream.h> /* defines queue_t */
829 #include <stropts.h> /* defines struct strtdinsert */
831 #define SPX_DEVICE "/dev/spx"
832 #define S_PIPE_HANDLE_ERRNO 1
833 /* if the above is defined to 1, we will attempt to
834 save and restore errno to indicate failure
835 reason to the caller;
836 Please note that this will not work in environments
837 where errno is not just an integer
840 #if S_PIPE_HANDLE_ERRNO
842 /* This is for "extern int errno;" */
845 /* s_pipe returns 0 if OK, -1 on error */
846 /* two file descriptors are returned */
847 static int s_pipe(int fd
[2])
849 struct strfdinsert ins
; /* stream I_FDINSERT ioctl format */
851 #if S_PIPE_HANDLE_ERRNO
855 * First open the stream clone device "dev/spx" twice,
856 * obtaining the two file descriptors
859 if ( (fd
[0] = open(SPX_DEVICE
, O_RDWR
)) < 0)
861 if ( (fd
[1] = open(SPX_DEVICE
, O_RDWR
)) < 0) {
862 #if S_PIPE_HANDLE_ERRNO
866 #if S_PIPE_HANDLE_ERRNO
873 * Now link these two stream together with an I_FDINSERT ioctl.
876 ins
.ctlbuf
.buf
= (char *) &pointer
; /* no control information, just the pointer */
877 ins
.ctlbuf
.maxlen
= sizeof pointer
;
878 ins
.ctlbuf
.len
= sizeof pointer
;
879 ins
.databuf
.buf
= (char *) 0; /* no data to be sent */
880 ins
.databuf
.maxlen
= 0;
881 ins
.databuf
.len
= -1; /* magic: must be -1 rather than 0 for stream pipes */
883 ins
.fildes
= fd
[1]; /* the fd to connect with fd[0] */
884 ins
.flags
= 0; /* nonpriority message */
885 ins
.offset
= 0; /* offset of pointer in control buffer */
887 if (ioctl(fd
[0], I_FDINSERT
, (char *) &ins
) < 0) {
888 #if S_PIPE_HANDLE_ERRNO
893 #if S_PIPE_HANDLE_ERRNO
898 /* all is OK if we came here, indicate success */
902 int socketpair(int dummy1
, int dummy2
, int dummy3
, int fd
[2])
907 #endif /* ifndef HAVE_SOCKETPAIR */
908 #endif /* ifdef USE_NETCODE */
909 #endif /* SCO_FLAVOR */