diff --git a/capplets/about-me/mate-about-me-password.c b/capplets/about-me/mate-about-me-password.c index abac117..5a82566 100644 --- a/capplets/about-me/mate-about-me-password.c +++ b/capplets/about-me/mate-about-me-password.c @@ -27,6 +27,14 @@ # include #endif +/* This is needed to get ptmx-related functions in GLIBC */ +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 600 +#endif + +/* This is needed to get pipe2() in GLIBC */ +#define _GNU_SOURCE + /* Are all of these needed? */ #include #include @@ -35,6 +43,16 @@ #include #include #include +#include +#include +#include +#include + +#define PASSWD "/usr/bin/passwd" +#define PTMX "/dev/ptmx" + +/* /dev/pts/N */ +#define PTY_MAX_NAME 20 #if __sun #include @@ -85,6 +103,9 @@ typedef struct { /* Buffer size for backend output */ #define BUFSIZE 64 +/* Bufffer size for backend error messages */ +#define ERRBUFSIZE 1024 + /* * Error handling {{ */ @@ -170,85 +191,178 @@ static gboolean spawn_passwd (PasswordDialog *pdialog, GError **error) { gchar *argv[2]; - gchar *envp[1]; - gint my_stdin, my_stdout, my_stderr; + int pid, pty_m, err_pipe[2]; + char slave_name[PTY_MAX_NAME]; + char *name; - argv[0] = "/usr/bin/passwd"; /* Is it safe to rely on a hard-coded path? */ + argv[0] = PASSWD; /* Is it safe to rely on a hard-coded path? */ argv[1] = NULL; - envp[0] = NULL; /* If we pass an empty array as the environment, - * will the childs environment be empty, and the - * locales set to the C default? From the manual: - * "If envp is NULL, the child inherits its - * parent'senvironment." - * If I'm wrong here, we somehow have to set - * the locales here. - */ - - if (!g_spawn_async_with_pipes (NULL, /* Working directory */ - argv, /* Argument vector */ - envp, /* Environment */ - G_SPAWN_DO_NOT_REAP_CHILD, /* Flags */ - NULL, /* Child setup */ - NULL, /* Data to child setup */ - &pdialog->backend_pid, /* PID */ - &my_stdin, /* Stdin */ - &my_stdout, /* Stdout */ - &my_stderr, /* Stderr */ - error)) { /* GError */ - - /* An error occurred */ - free_passwd_resources (pdialog); - + pty_m = open(PTMX, O_RDWR|O_NOCTTY); + if (pty_m > 0) { + name = ptsname(pty_m); + if (name && (strlen(name) < PTY_MAX_NAME)) { + strncpy(slave_name, name, PTY_MAX_NAME); + } else { + g_set_error(error, + PASSDLG_ERROR, + PASSDLG_ERROR_BACKEND, + _("Couldn't get slave_name of pty")); + close(pty_m); + return FALSE; + } + } else { + g_set_error (error, + PASSDLG_ERROR, + PASSDLG_ERROR_BACKEND, + _("Couldn't open pseudo-tty")); return FALSE; } - /* 2>&1 */ - if (dup2 (my_stderr, my_stdout) == -1) { - /* Failed! */ + if (grantpt(pty_m) < 0) { g_set_error (error, - PASSDLG_ERROR, - PASSDLG_ERROR_BACKEND, - "%s", - strerror (errno)); - - /* Clean up */ - stop_passwd (pdialog); - + PASSDLG_ERROR, + PASSDLG_ERROR_BACKEND, + _("Couldn't set permission on slave device: %s"), + strerror(errno)); + close(pty_m); return FALSE; } - /* Open IO Channels */ - pdialog->backend_stdin = g_io_channel_unix_new (my_stdin); - pdialog->backend_stdout = g_io_channel_unix_new (my_stdout); - - /* Set raw encoding */ - /* Set nonblocking mode */ - if (g_io_channel_set_encoding (pdialog->backend_stdin, NULL, error) != G_IO_STATUS_NORMAL || - g_io_channel_set_encoding (pdialog->backend_stdout, NULL, error) != G_IO_STATUS_NORMAL || - g_io_channel_set_flags (pdialog->backend_stdin, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL || - g_io_channel_set_flags (pdialog->backend_stdout, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL ) { + if (unlockpt(pty_m) < 0) { + g_set_error (error, + PASSDLG_ERROR, + PASSDLG_ERROR_BACKEND, + _("Couldn't unlock slave device: %s"), + strerror(errno)); + close(pty_m); + return FALSE; + } - /* Clean up */ - stop_passwd (pdialog); + if (pipe2(err_pipe, O_CLOEXEC) < 0) { + g_set_error (error, + PASSDLG_ERROR, + PASSDLG_ERROR_BACKEND, + _("Couldn't create error reporting pipe: %s"), + strerror(errno)); + close(pty_m); return FALSE; } + + pid = fork(); + if (pid == 0) { + /* Child */ + int pty_s, len; + char buf[ERRBUFSIZE]; + close(err_pipe[0]); + + close(pty_m); + if (setsid() < 0) { + len = snprintf(buf, ERRBUFSIZE, _("Couldn't create new process group: %s"), strerror(errno)); + write(err_pipe[1], buf, (len>ERRBUFSIZE) ? ERRBUFSIZE : len ); + exit(1); + } - /* Turn off buffering */ - g_io_channel_set_buffered (pdialog->backend_stdin, FALSE); - g_io_channel_set_buffered (pdialog->backend_stdout, FALSE); + /* Now we are a session leader, on System V first open tty will become controlling tty */ + pty_s = open(slave_name, O_RDWR); - /* Add IO Channel watcher */ - pdialog->backend_stdout_watch_id = g_io_add_watch (pdialog->backend_stdout, - G_IO_IN | G_IO_PRI, - (GIOFunc) io_watch_stdout, pdialog); + if (pty_s < 0) { + len = snprintf(buf, ERRBUFSIZE, _("Couldn't open slave terminal device: %s"), strerror(errno)); + write(err_pipe[1], buf, (len>ERRBUFSIZE) ? ERRBUFSIZE : len ); + exit(1); + } + +#if defined(TIOCSCTTY) && !defined(__sun) + /* BSD systems need to set controlling tty explicitly. Solaris/illumos can export + TIOCSCTTY when __EXTENSIONS__ are defined, but doesn't need this. */ + if (ioctl(pty_s,TIOCSCTTY, NULL) < 0) { + close(pty_s); + len=snprintf(buf, ERRBUFSIZE, _("Couldn't establish controlling terminal: %s"), strerror(errno)); + write(err_pipe[1], buf, (len>ERRBUFSIZE) ? ERRBUFSIZE : len ); + exit(1); + } +#endif + + /* Set stdin, stdout, stderr to our tty */ + dup2(pty_s, 0); + dup2(pty_s, 1); + dup2(pty_s, 2); + + execvp(argv[0], argv); + + /* Error */ + close(pty_s); + len=snprintf(buf, ERRBUFSIZE, _("Couldn't exec passwd: %s"), strerror(errno)); + write(err_pipe[1], buf, (len>ERRBUFSIZE) ? ERRBUFSIZE : len ); + exit(1); + } else if (pid > 0) { + int rb, pos = 0; + char buf[ERRBUFSIZE]; + + + close(err_pipe[1]); + /* Wait for child to run exec() and read possible error message */ + while (pos < ERRBUFSIZE - 1 && ((rb=read(err_pipe[0], &buf[pos], ERRBUFSIZE-1-pos)) > 0)) { + pos += rb; + } + close(err_pipe[0]); + if (pos > 0) { + /* There were messages in err_pipe */ + buf[pos+1] = '\0'; + g_set_error (error, + PASSDLG_ERROR, + PASSDLG_ERROR_BACKEND, + buf); + + stop_passwd(pdialog); + return FALSE; + } + + /* Open IO Channels */ + pdialog->backend_stdin = g_io_channel_unix_new (pty_m); + /* g_io_channel_shutdown(pdialog->backend_stdin) will close associated file descriptor (pty_m), + but this will generate warning on g_io_channel_shutdown(pdialog->backend_stdout). + To avoid it, dup() file descriptor */ + pdialog->backend_stdout = g_io_channel_unix_new (dup(pty_m)); + pdialog->backend_pid = pid; + + /* Set raw encoding */ + /* Set nonblocking mode */ + if (g_io_channel_set_encoding (pdialog->backend_stdin, NULL, error) != G_IO_STATUS_NORMAL || + g_io_channel_set_encoding (pdialog->backend_stdout, NULL, error) != G_IO_STATUS_NORMAL || + g_io_channel_set_flags (pdialog->backend_stdin, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL || + g_io_channel_set_flags (pdialog->backend_stdout, G_IO_FLAG_NONBLOCK, error) != G_IO_STATUS_NORMAL ) { + + /* Clean up */ + stop_passwd (pdialog); + return FALSE; + } - /* Add child watcher */ - pdialog->backend_child_watch_id = g_child_watch_add (pdialog->backend_pid, (GChildWatchFunc) child_watch_cb, pdialog); + /* Turn off buffering */ + g_io_channel_set_buffered (pdialog->backend_stdin, FALSE); + g_io_channel_set_buffered (pdialog->backend_stdout, FALSE); - /* Success! */ + /* Add IO Channel watcher */ + pdialog->backend_stdout_watch_id = g_io_add_watch (pdialog->backend_stdout, + G_IO_IN | G_IO_PRI, + (GIOFunc) io_watch_stdout, pdialog); + + /* Add child watcher */ + pdialog->backend_child_watch_id = g_child_watch_add (pdialog->backend_pid, (GChildWatchFunc) child_watch_cb, pdialog); - return TRUE; + /* Success! */ + + return TRUE; + } else { + /* Error */ + g_set_error(error, + PASSDLG_ERROR, + PASSDLG_ERROR_BACKEND, + _("Couldn't fork: %s"), + strerror(errno)); + close(pty_m); + return FALSE; + } } /* Stop passwd backend */ @@ -822,7 +936,7 @@ passdlg_spawn_passwd (PasswordDialog *pdialog) /* translators: Unable to launch : */ details = g_strdup_printf (_("Unable to launch %s: %s"), - "/usr/bin/passwd", error->message); + PASSWD, error->message); passdlg_error_dialog (GTK_WINDOW (parent), _("Unable to launch backend"),