Merging of my tree.
This contains: * a few capplets were redone UI wise: ui-properties keyboard background-properties * splitting of libcapplet out of this modules (into libcapplet) * new html-based view in the control-center * initial import of functional root-manager This stuff isn't all done yet, but I wanted to get it in. TODO files will hopefully follow.
This commit is contained in:
parent
2749ba455b
commit
d42affc965
73 changed files with 6567 additions and 3322 deletions
6
root-manager/.cvsignore
Normal file
6
root-manager/.cvsignore
Normal file
|
@ -0,0 +1,6 @@
|
|||
Makefile
|
||||
Makefile.in
|
||||
.libs
|
||||
.deps
|
||||
root-manager
|
||||
root-manager-helper
|
26
root-manager/Makefile.am
Normal file
26
root-manager/Makefile.am
Normal file
|
@ -0,0 +1,26 @@
|
|||
INCLUDES = \
|
||||
-DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
|
||||
-I$(top_srcdir)/intl -I$(top_builddir)/intl \
|
||||
-I$(includedir) $(shell gtk-config --cflags) \
|
||||
-DDEBUG_USERHELPER
|
||||
|
||||
bin_PROGRAMS = root-manager-helper
|
||||
|
||||
sbin_PROGRAMS = root-manager
|
||||
|
||||
root_manager_SOURCES = \
|
||||
root-manager.c \
|
||||
root-manager.h
|
||||
|
||||
root_manager_LDADD = -lpam -lpam_misc -ldl
|
||||
|
||||
root_manager_LDFLAGS = $(shell glib-config --libs)
|
||||
|
||||
root_manager_helper_SOURCES = \
|
||||
root-manager-helper.c \
|
||||
root-manager-wrap.c \
|
||||
root-manager-wrap.h \
|
||||
userdialogs.c \
|
||||
userdialogs.h
|
||||
|
||||
root_manager_helper_LDFLAGS = $(shell gtk-config --libs)
|
1
root-manager/TODO
Normal file
1
root-manager/TODO
Normal file
|
@ -0,0 +1 @@
|
|||
1: write TODO file
|
71
root-manager/root-manager-helper.c
Normal file
71
root-manager/root-manager-helper.c
Normal file
|
@ -0,0 +1,71 @@
|
|||
/* Copyright (C) 1999 Red Hat Software, Inc.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <locale.h>
|
||||
#include <libintl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include "userdialogs.h"
|
||||
#include "root-manager-wrap.h"
|
||||
|
||||
void
|
||||
userhelper_fatal_error(int signal)
|
||||
{
|
||||
gtk_main_quit();
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
char *argv2[] = { UH_PATH, "0", NULL };
|
||||
char **argv_fake;
|
||||
int argc_fake = 2;
|
||||
|
||||
int new_fd;
|
||||
|
||||
/* first set up our locale info for gettext. */
|
||||
setlocale (LC_ALL, "");
|
||||
bindtextdomain (PACKAGE, GNOMELOCALEDIR);
|
||||
textdomain (PACKAGE);
|
||||
|
||||
argv_fake = g_new0 (char *, 3);
|
||||
argv_fake[0] = UH_PATH;
|
||||
argv_fake[1] = "0";
|
||||
|
||||
if (!gtk_init_check(&argc_fake, &argv_fake)) {
|
||||
g_error ("Must be run with $DISPLAY");
|
||||
}
|
||||
|
||||
new_fd = dup (STDIN_FILENO);
|
||||
if (new_fd < 0)
|
||||
g_error ("error dup()'ing");
|
||||
|
||||
signal(SIGCHLD, userhelper_fatal_error);
|
||||
|
||||
userhelper_runv (UH_PATH, new_fd);
|
||||
|
||||
gtk_main();
|
||||
|
||||
g_print ("back...\n");
|
||||
|
||||
return 0;
|
||||
}
|
528
root-manager/root-manager-wrap.c
Normal file
528
root-manager/root-manager-wrap.c
Normal file
|
@ -0,0 +1,528 @@
|
|||
/* -*-Mode: c-*- */
|
||||
/* Copyright (C) 1997 Red Hat Software, Inc.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <locale.h>
|
||||
#include <libintl.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <gdk/gdkx.h>
|
||||
#include "root-manager-wrap.h"
|
||||
#include "userdialogs.h"
|
||||
|
||||
#define MAXLINE 512
|
||||
|
||||
int childout[2];
|
||||
int childin[2];
|
||||
int childout_tag;
|
||||
|
||||
void
|
||||
userhelper_runv(char *path, int new_fd)
|
||||
{
|
||||
pid_t pid;
|
||||
int retval;
|
||||
int i;
|
||||
int stdin_save = STDIN_FILENO;
|
||||
int stdout_save = STDOUT_FILENO;
|
||||
int stderr_save = STDERR_FILENO;
|
||||
char *nargs[256]; /* only used internally, we know this will not overflow */
|
||||
|
||||
if((pipe(childout) == -1) || (pipe(childin) == -1))
|
||||
{
|
||||
fprintf(stderr, _("Pipe error.\n"));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if((pid = fork()) < 0)
|
||||
{
|
||||
fprintf(stderr, _("Cannot fork().\n"));
|
||||
}
|
||||
else if(pid > 0) /* parent */
|
||||
{
|
||||
close(childout[1]);
|
||||
close(childin[0]);
|
||||
close (new_fd);
|
||||
|
||||
childout_tag = gdk_input_add(childout[0], GDK_INPUT_READ, (GdkInputFunction) userhelper_read_childout, NULL);
|
||||
|
||||
}
|
||||
else /* child */
|
||||
{
|
||||
close(childout[0]);
|
||||
close(childin[1]);
|
||||
|
||||
if(childout[1] != STDOUT_FILENO)
|
||||
{
|
||||
if(((stdout_save = dup(STDOUT_FILENO)) == -1) ||
|
||||
(dup2(childout[1], STDOUT_FILENO) != STDOUT_FILENO))
|
||||
{
|
||||
fprintf(stderr, _("dup2() error.\n"));
|
||||
exit(2);
|
||||
}
|
||||
close(childout[1]);
|
||||
}
|
||||
if(childin[0] != STDIN_FILENO)
|
||||
{
|
||||
if(((stdin_save = dup(STDIN_FILENO)) == -1) ||
|
||||
(dup2(childin[0], STDIN_FILENO) != STDIN_FILENO))
|
||||
{
|
||||
fprintf(stderr, _("dup2() error.\n"));
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
memset(&nargs, 0, sizeof(nargs));
|
||||
nargs[0] = UH_PATH;
|
||||
nargs[1] = g_strdup_printf ("%d", new_fd);
|
||||
#if 0
|
||||
nargs[1] = "-d";
|
||||
nargs[2] = g_strdup_printf("%d,%d,%d", stdin_save, stdout_save,
|
||||
stderr_save);
|
||||
for(i = 3; i < sizeof(nargs) / sizeof(nargs[0]); i++) {
|
||||
nargs[i] = args[i - 2];
|
||||
if(nargs[i] == NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef DEBUG_USERHELPER
|
||||
for(i = 0; i < sizeof(nargs) / sizeof(nargs[0]); i++) {
|
||||
if(nargs[i] == NULL) {
|
||||
break;
|
||||
}
|
||||
fprintf(stderr, "Exec arg = \"%s\".\n", nargs[i]);
|
||||
}
|
||||
#endif
|
||||
retval = execv(path, nargs);
|
||||
|
||||
if(retval < 0) {
|
||||
fprintf(stderr, _("execl() error, errno=%d\n"), errno);
|
||||
}
|
||||
|
||||
_exit(0);
|
||||
|
||||
}
|
||||
}
|
||||
void
|
||||
userhelper_run(char *path, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *args[256]; /* only used internally, we know this will not overflow */
|
||||
int i = 0;
|
||||
|
||||
va_start(ap, path);
|
||||
while((i < 255) && ((args[i++] = va_arg(ap, char *)) != NULL));
|
||||
va_end(ap);
|
||||
|
||||
userhelper_runv(path, args);
|
||||
}
|
||||
|
||||
void
|
||||
userhelper_parse_exitstatus(int exitstatus)
|
||||
{
|
||||
GtkWidget* message_box;
|
||||
|
||||
switch(exitstatus)
|
||||
{
|
||||
case 0:
|
||||
message_box = create_message_box(_("Information updated."), NULL);
|
||||
break;
|
||||
case ERR_PASSWD_INVALID:
|
||||
message_box = create_error_box(_("The password you typed is invalid.\nPlease try again."), NULL);
|
||||
break;
|
||||
case ERR_FIELDS_INVALID:
|
||||
message_box = create_error_box(_("One or more of the changed fields is invalid.\nThis is probably due to either colons or commas in one of the fields.\nPlease remove those and try again."), NULL);
|
||||
break;
|
||||
case ERR_SET_PASSWORD:
|
||||
message_box = create_error_box(_("Password resetting error."), NULL);
|
||||
break;
|
||||
case ERR_LOCKS:
|
||||
message_box = create_error_box(_("Some systems files are locked.\nPlease try again in a few moments."), NULL);
|
||||
break;
|
||||
case ERR_NO_USER:
|
||||
message_box = create_error_box(_("Unknown user."), NULL);
|
||||
break;
|
||||
case ERR_NO_RIGHTS:
|
||||
message_box = create_error_box(_("Insufficient rights."), NULL);
|
||||
break;
|
||||
case ERR_INVALID_CALL:
|
||||
message_box = create_error_box(_("Invalid call to sub process."), NULL);
|
||||
break;
|
||||
case ERR_SHELL_INVALID:
|
||||
message_box = create_error_box(_("Your current shell is not listed in /etc/shells.\nYou are not allowed to change your shell.\nConsult your system administrator."), NULL);
|
||||
break;
|
||||
case ERR_NO_MEMORY:
|
||||
/* well, this is unlikely to work either, but at least we tried... */
|
||||
message_box = create_error_box(_("Out of memory."), NULL);
|
||||
break;
|
||||
case ERR_EXEC_FAILED:
|
||||
message_box = create_error_box(_("The exec() call failed."), NULL);
|
||||
break;
|
||||
case ERR_NO_PROGRAM:
|
||||
message_box = create_error_box(_("Failed to find selected program."), NULL);
|
||||
break;
|
||||
case ERR_UNK_ERROR:
|
||||
message_box = create_error_box(_("Unknown error."), NULL);
|
||||
break;
|
||||
default:
|
||||
message_box = create_error_box(_("Unknown exit code."), NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
gtk_signal_connect(GTK_OBJECT(message_box), "destroy", (GtkSignalFunc) userhelper_fatal_error, NULL);
|
||||
gtk_signal_connect(GTK_OBJECT(message_box), "delete_event", (GtkSignalFunc) userhelper_fatal_error, NULL);
|
||||
gtk_widget_show(message_box);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
userhelper_grab_focus(GtkWidget *widget, GdkEvent *map_event, gpointer data)
|
||||
{
|
||||
int ret;
|
||||
GtkWidget *toplevel;
|
||||
/* grab focus for the toplevel of this widget, so that peers can
|
||||
* get events too */
|
||||
toplevel = gtk_widget_get_toplevel(widget);
|
||||
ret = gdk_keyboard_grab(toplevel->window, TRUE, GDK_CURRENT_TIME);
|
||||
}
|
||||
|
||||
static void
|
||||
mark_void(GtkWidget *widget, gpointer target)
|
||||
{
|
||||
if(target != NULL) {
|
||||
*(gpointer*)target = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
userhelper_parse_childout(char* outline)
|
||||
{
|
||||
char *prompt;
|
||||
char *rest = NULL;
|
||||
char *title;
|
||||
int prompt_type;
|
||||
static response *resp = NULL;
|
||||
GdkPixmap *pixmap;
|
||||
|
||||
if (resp != NULL) {
|
||||
if(!GTK_IS_WINDOW(resp->top)) {
|
||||
g_free(resp->user);
|
||||
g_free(resp);
|
||||
resp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (resp == NULL) {
|
||||
GtkWidget *vbox, *hbox, *sbox;
|
||||
|
||||
resp = g_malloc0(sizeof(response));
|
||||
|
||||
resp->user = g_strdup(getlogin());
|
||||
resp->top = gtk_window_new(GTK_WINDOW_DIALOG);
|
||||
gtk_signal_connect(GTK_OBJECT(resp->top), "destroy",
|
||||
GTK_SIGNAL_FUNC(mark_void), &resp->top);
|
||||
gtk_window_set_title(GTK_WINDOW(resp->top), _("Input"));
|
||||
gtk_window_position(GTK_WINDOW(resp->top), GTK_WIN_POS_CENTER);
|
||||
gtk_container_set_border_width(GTK_CONTAINER(resp->top), 5);
|
||||
gtk_signal_connect(GTK_OBJECT(resp->top), "map",
|
||||
GTK_SIGNAL_FUNC(userhelper_grab_focus), NULL);
|
||||
|
||||
resp->table = gtk_table_new(1, 2, FALSE);
|
||||
resp->rows = 1;
|
||||
|
||||
vbox = gtk_vbox_new(FALSE, 4);
|
||||
sbox = gtk_hbox_new(TRUE, 4);
|
||||
hbox = gtk_hbutton_box_new();
|
||||
gtk_object_set_data(GTK_OBJECT(resp->top), UH_ACTION_AREA, hbox);
|
||||
gtk_box_pack_start(GTK_BOX(vbox), resp->table, TRUE, TRUE, 4);
|
||||
gtk_box_pack_start(GTK_BOX(vbox), gtk_hseparator_new(), FALSE, FALSE, 4);
|
||||
gtk_box_pack_start(GTK_BOX(vbox), sbox, FALSE, FALSE, 4);
|
||||
gtk_box_pack_start(GTK_BOX(sbox), hbox, FALSE, FALSE, 4);
|
||||
gtk_container_add(GTK_CONTAINER(resp->top), vbox);
|
||||
|
||||
pixmap = gdk_pixmap_create_from_xpm(gdk_window_foreign_new(GDK_ROOT_WINDOW()),
|
||||
NULL, NULL, UH_KEY_PIXMAP_PATH);
|
||||
if(pixmap != NULL) {
|
||||
GtkWidget *pm = gtk_pixmap_new(pixmap, NULL);
|
||||
if(pm != NULL) {
|
||||
GtkWidget *frame = NULL;
|
||||
frame = gtk_frame_new(NULL);
|
||||
gtk_container_add(GTK_CONTAINER(frame), pm);
|
||||
gtk_table_attach(GTK_TABLE(resp->table), frame,
|
||||
0, 1, 1, 2, GTK_SHRINK, GTK_SHRINK, 2, 2);
|
||||
resp->left = 1;
|
||||
}
|
||||
}
|
||||
|
||||
resp->ok = gtk_button_new_with_label(_(UD_OK_TEXT));
|
||||
gtk_misc_set_padding(GTK_MISC(GTK_BIN(resp->ok)->child), 4, 0);
|
||||
gtk_box_pack_start(GTK_BOX(hbox), resp->ok, FALSE, FALSE, 0);
|
||||
|
||||
resp->cancel = gtk_button_new_with_label(_(UD_CANCEL_TEXT));
|
||||
gtk_misc_set_padding(GTK_MISC(GTK_BIN(resp->cancel)->child), 4, 0);
|
||||
gtk_box_pack_start(GTK_BOX(hbox), resp->cancel, FALSE, FALSE, 0);
|
||||
|
||||
gtk_signal_connect(GTK_OBJECT(resp->top), "delete_event",
|
||||
(GtkSignalFunc) userhelper_fatal_error, NULL);
|
||||
gtk_signal_connect(GTK_OBJECT(resp->cancel), "clicked",
|
||||
(GtkSignalFunc) userhelper_fatal_error, NULL);
|
||||
|
||||
gtk_signal_connect(GTK_OBJECT(resp->ok), "clicked",
|
||||
(GtkSignalFunc) userhelper_write_childin, resp);
|
||||
|
||||
gtk_object_set_user_data(GTK_OBJECT(resp->top), resp);
|
||||
}
|
||||
|
||||
if(isdigit(outline[0])) {
|
||||
gboolean echo = TRUE;
|
||||
message *msg = g_malloc(sizeof(message));
|
||||
|
||||
prompt_type = strtol(outline, &prompt, 10);
|
||||
if((prompt != NULL) && (strlen(prompt) > 0)) {
|
||||
while((isspace(prompt[0]) && (prompt[0] != '\0') && (prompt[0] != '\n'))){
|
||||
prompt++;
|
||||
}
|
||||
}
|
||||
|
||||
/* snip off terminating newlines in the prompt string and save a pointer to
|
||||
* interate the parser along */
|
||||
rest = strchr(prompt, '\n');
|
||||
if(rest != NULL) {
|
||||
*rest = '\0';
|
||||
rest++;
|
||||
if (rest[0] == '\0') {
|
||||
rest = NULL;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG_USERHELPER
|
||||
g_print("(%d) \"%s\"\n", prompt_type, prompt);
|
||||
#endif
|
||||
|
||||
msg->type = prompt_type;
|
||||
msg->message = prompt;
|
||||
msg->data = NULL;
|
||||
msg->entry = NULL;
|
||||
|
||||
echo = TRUE;
|
||||
switch(prompt_type) {
|
||||
case UH_ECHO_OFF_PROMPT:
|
||||
echo = FALSE;
|
||||
/* fall through */
|
||||
case UH_ECHO_ON_PROMPT:
|
||||
msg->label = gtk_label_new(prompt);
|
||||
gtk_label_set_line_wrap(GTK_LABEL(msg->label), FALSE);
|
||||
gtk_misc_set_alignment(GTK_MISC(msg->label), 1.0, 1.0);
|
||||
|
||||
msg->entry = gtk_entry_new();
|
||||
gtk_entry_set_visibility(GTK_ENTRY(msg->entry), echo);
|
||||
|
||||
if(resp->head == NULL) resp->head = msg->entry;
|
||||
resp->tail = msg->entry;
|
||||
|
||||
gtk_table_attach(GTK_TABLE(resp->table), msg->label,
|
||||
resp->left + 0, resp->left + 1, resp->rows, resp->rows + 1,
|
||||
GTK_EXPAND | GTK_FILL, 0, 2, 2);
|
||||
gtk_table_attach(GTK_TABLE(resp->table), msg->entry,
|
||||
resp->left + 1, resp->left + 2, resp->rows, resp->rows + 1,
|
||||
GTK_EXPAND | GTK_FILL, 0, 2, 2);
|
||||
|
||||
resp->message_list = g_slist_append(resp->message_list, msg);
|
||||
resp->responses++;
|
||||
resp->rows++;
|
||||
#ifdef DEBUG_USERHELPER
|
||||
g_print(_("Need %d responses.\n"), resp->responses);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case UH_FALLBACK:
|
||||
#if 0
|
||||
msg->label = gtk_label_new(prompt);
|
||||
gtk_label_set_line_wrap(GTK_LABEL(msg->label), FALSE);
|
||||
gtk_table_attach(GTK_TABLE(resp->table), msg->label,
|
||||
resp->left + 0, resp->left + 2, resp->rows, resp->rows + 1,
|
||||
0, 0, 2, 2);
|
||||
resp->message_list = g_slist_append(resp->message_list, msg);
|
||||
#else
|
||||
resp->fallback = atoi(prompt) != 0;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case UH_USER:
|
||||
if(strstr(prompt, "<user>") == NULL) {
|
||||
g_free(resp->user);
|
||||
resp->user = g_strdup(prompt);
|
||||
}
|
||||
break;
|
||||
|
||||
case UH_SERVICE_NAME:
|
||||
title = g_strdup_printf(_("In order to run \"%s\" with root's "
|
||||
"privileges, additional information is "
|
||||
"required."),
|
||||
prompt);
|
||||
msg->label = gtk_label_new(title);
|
||||
gtk_label_set_line_wrap(GTK_LABEL(msg->label), FALSE);
|
||||
gtk_table_attach(GTK_TABLE(resp->table), msg->label,
|
||||
0, resp->left + 2, 0, 1,
|
||||
GTK_EXPAND | GTK_FILL, 0, 2, 2);
|
||||
resp->message_list = g_slist_append(resp->message_list, msg);
|
||||
break;
|
||||
|
||||
case UH_ERROR_MSG:
|
||||
gtk_window_set_title(GTK_WINDOW(resp->top), _("Error"));
|
||||
/* fall through */
|
||||
case UH_INFO_MSG:
|
||||
msg->label = gtk_label_new(prompt);
|
||||
gtk_label_set_line_wrap(GTK_LABEL(msg->label), FALSE);
|
||||
gtk_table_attach(GTK_TABLE(resp->table), msg->label,
|
||||
resp->left + 0, resp->left + 2, resp->rows, resp->rows + 1, 0, 0, 2, 2);
|
||||
resp->message_list = g_slist_append(resp->message_list, msg);
|
||||
resp->rows++;
|
||||
break;
|
||||
|
||||
case UH_EXPECT_RESP:
|
||||
g_free(msg); /* useless */
|
||||
if (resp->responses != atoi(prompt)) {
|
||||
g_print(_("You want %d response(s) from %d entry fields!?!?!\n"),
|
||||
atoi(prompt), resp->responses);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (resp->fallback) {
|
||||
gpointer a = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(resp->top),
|
||||
UH_ACTION_AREA));
|
||||
GtkWidget *hbox = GTK_WIDGET(a);
|
||||
resp->unprivileged = gtk_button_new_with_label(_(UD_FALLBACK_TEXT));
|
||||
gtk_misc_set_padding(GTK_MISC(GTK_BIN(resp->unprivileged)->child),
|
||||
4, 0);
|
||||
gtk_box_pack_start(GTK_BOX(hbox), resp->unprivileged,
|
||||
FALSE, FALSE, 0);
|
||||
if(resp->unprivileged != NULL) {
|
||||
gtk_signal_connect(GTK_OBJECT(resp->unprivileged), "clicked",
|
||||
GTK_SIGNAL_FUNC(userhelper_write_childin), resp);
|
||||
}
|
||||
}
|
||||
|
||||
gtk_widget_show_all(resp->top);
|
||||
if(GTK_IS_ENTRY(resp->head)) {
|
||||
gtk_widget_grab_focus(resp->head);
|
||||
}
|
||||
if(GTK_IS_ENTRY(resp->tail)) {
|
||||
gtk_signal_connect_object(GTK_OBJECT(resp->tail), "activate",
|
||||
gtk_button_clicked, GTK_OBJECT(resp->ok));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* ignore, I guess... */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(rest != NULL) userhelper_parse_childout(rest);
|
||||
}
|
||||
|
||||
void
|
||||
userhelper_read_childout(gpointer data, int source, GdkInputCondition cond)
|
||||
{
|
||||
char* output;
|
||||
int count;
|
||||
|
||||
if(cond != GDK_INPUT_READ)
|
||||
{
|
||||
/* Serious error, this is. Panic. */
|
||||
exit (1);
|
||||
}
|
||||
|
||||
output = g_malloc(MAXLINE + 1);
|
||||
|
||||
count = read(source, output, MAXLINE);
|
||||
if (count == -1)
|
||||
{
|
||||
exit (0);
|
||||
}
|
||||
if (count == 0)
|
||||
{
|
||||
gdk_input_remove(childout_tag);
|
||||
childout_tag = -1;
|
||||
}
|
||||
output[count] = '\0';
|
||||
|
||||
userhelper_parse_childout(output);
|
||||
g_free(output);
|
||||
}
|
||||
|
||||
void
|
||||
userhelper_write_childin(GtkWidget *widget, response *resp)
|
||||
{
|
||||
char* input;
|
||||
int len;
|
||||
guchar byte;
|
||||
GSList *message_list = resp->message_list;
|
||||
|
||||
if(widget == resp->unprivileged) {
|
||||
byte = UH_ABORT;
|
||||
for (message_list = resp->message_list;
|
||||
(message_list != NULL) && (message_list->data != NULL);
|
||||
message_list = g_slist_next(message_list)) {
|
||||
message *m = (message*)message_list->data;
|
||||
#ifdef DEBUG_USERHELPER
|
||||
fprintf(stderr, "message %d, \"%s\"\n", m->type, m->message);
|
||||
#endif
|
||||
if(GTK_IS_ENTRY(m->entry)) {
|
||||
write(childin[1], &byte, 1);
|
||||
write(childin[1], "\n", 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(widget == resp->ok) {
|
||||
byte = UH_TEXT;
|
||||
for (message_list = resp->message_list;
|
||||
(message_list != NULL) && (message_list->data != NULL);
|
||||
message_list = g_slist_next(message_list)) {
|
||||
message *m = (message*)message_list->data;
|
||||
#ifdef DEBUG_USERHELPER
|
||||
fprintf(stderr, "message %d, \"%s\"\n", m->type, m->message);
|
||||
#endif
|
||||
if(GTK_IS_ENTRY(m->entry)) {
|
||||
input = gtk_entry_get_text(GTK_ENTRY(m->entry));
|
||||
len = strlen(input);
|
||||
write(childin[1], &byte, 1);
|
||||
write(childin[1], input, len);
|
||||
write(childin[1], "\n", 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
gtk_widget_destroy(resp->top);
|
||||
}
|
||||
|
||||
void
|
||||
userhelper_sigchld()
|
||||
{
|
||||
pid_t pid;
|
||||
int status;
|
||||
|
||||
signal(SIGCHLD, userhelper_sigchld);
|
||||
|
||||
pid = waitpid(0, &status, WNOHANG);
|
||||
|
||||
if(WIFEXITED(status))
|
||||
{
|
||||
userhelper_parse_exitstatus(WEXITSTATUS(status));
|
||||
}
|
||||
}
|
68
root-manager/root-manager-wrap.h
Normal file
68
root-manager/root-manager-wrap.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
/* Copyright (C) 1997-1999 Red Hat Software, Inc.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef __USERHELPER_WRAP_H__
|
||||
#define __USERHELPER_WRAP_H__
|
||||
|
||||
/* lots 'o includes. */
|
||||
#include "userdialogs.h"
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "root-manager.h"
|
||||
#define UH_ACTION_AREA "userhelper-action-area"
|
||||
|
||||
typedef struct message {
|
||||
int type;
|
||||
char *message;
|
||||
char *data;
|
||||
GtkWidget *entry;
|
||||
GtkWidget *label;
|
||||
} message;
|
||||
|
||||
typedef struct response {
|
||||
int responses;
|
||||
int left;
|
||||
int rows;
|
||||
gboolean fallback;
|
||||
char *user;
|
||||
GSList *message_list; /* contains pointers to messages */
|
||||
GtkWidget *head;
|
||||
GtkWidget *tail;
|
||||
GtkWidget *top;
|
||||
GtkWidget *table;
|
||||
GtkWidget *ok;
|
||||
GtkWidget *cancel;
|
||||
GtkWidget *unprivileged;
|
||||
} response;
|
||||
|
||||
void userhelper_run(char *path, ...);
|
||||
void userhelper_runv(char *path, int new_fd);
|
||||
void userhelper_parse_exitstatus(int exitstatus);
|
||||
void userhelper_parse_childout();
|
||||
void userhelper_read_childout(gpointer data, int source, GdkInputCondition cond);
|
||||
void userhelper_write_childin(GtkWidget* widget, response *resp);
|
||||
|
||||
void userhelper_sigchld(); /* sigchld handler */
|
||||
|
||||
void userhelper_fatal_error(int ignored);
|
||||
|
||||
#endif /* __USERHELPER_WRAP_H__ */
|
713
root-manager/root-manager.c
Normal file
713
root-manager/root-manager.c
Normal file
|
@ -0,0 +1,713 @@
|
|||
/* -*-Mode: c-*- */
|
||||
/* Copyright (C) 1997 Red Hat Software, Inc.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
/*
|
||||
* UserTool suid helper program
|
||||
*/
|
||||
|
||||
#if 0
|
||||
#define USE_MCHECK 1
|
||||
#endif
|
||||
|
||||
#define DEBUG_USERHELPER 1
|
||||
#define ROOT_MANAGER 1
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#ifdef USE_MCHECK
|
||||
#include <mcheck.h>
|
||||
#endif
|
||||
|
||||
#include <locale.h>
|
||||
#include <libintl.h>
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <security/pam_appl.h>
|
||||
#include <security/pam_misc.h>
|
||||
|
||||
#include "root-manager.h"
|
||||
#include "shvar.h"
|
||||
|
||||
/* Total GECOS field length... is this enough ? */
|
||||
#define GECOS_LENGTH 80
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
|
||||
/* ------ some static data objects ------- */
|
||||
|
||||
static char *full_name = NULL; /* full user name */
|
||||
static char *office = NULL; /* office */
|
||||
static char *office_ph = NULL; /* office phone */
|
||||
static char *home_ph = NULL; /* home phone */
|
||||
static char *user_name = NULL; /* the account name */
|
||||
static char *shell_path = NULL; /* shell path */
|
||||
|
||||
static char *the_username = NULL; /* used to mangle the conversation function */
|
||||
|
||||
/* manipulate the environment directly */
|
||||
extern char **environ;
|
||||
|
||||
/* command line flags */
|
||||
static int f_flg = 0; /* -f flag = change full name */
|
||||
static int o_flg = 0; /* -o flag = change office name */
|
||||
static int p_flg = 0; /* -p flag = change office phone */
|
||||
static int h_flg = 0; /* -h flag = change home phone number */
|
||||
static int c_flg = 0; /* -c flag = change password */
|
||||
static int s_flg = 0; /* -s flag = change shell */
|
||||
static int t_flg = 0; /* -t flag = direct text-mode -- exec'ed */
|
||||
static int w_flg = 0; /* -w flag = act as a wrapper for next args */
|
||||
static int d_flg = 0; /* -d flag = three descriptor numbers for us */
|
||||
|
||||
/*
|
||||
* A handy fail exit function we can call from many places
|
||||
*/
|
||||
static int fail_error(int retval)
|
||||
{
|
||||
/* this is a temporary kludge.. will be fixed shortly. */
|
||||
if(retval == ERR_SHELL_INVALID)
|
||||
exit(ERR_SHELL_INVALID);
|
||||
|
||||
if (retval != PAM_SUCCESS) {
|
||||
#ifdef DEBUG_USERHELPER
|
||||
g_print(_("Got error %d.\n"), retval);
|
||||
#endif
|
||||
switch(retval) {
|
||||
case PAM_AUTH_ERR:
|
||||
case PAM_PERM_DENIED:
|
||||
exit (ERR_PASSWD_INVALID);
|
||||
case PAM_AUTHTOK_LOCK_BUSY:
|
||||
exit (ERR_LOCKS);
|
||||
case PAM_CRED_INSUFFICIENT:
|
||||
case PAM_AUTHINFO_UNAVAIL:
|
||||
exit (ERR_NO_RIGHTS);
|
||||
case PAM_ABORT:
|
||||
default:
|
||||
exit(ERR_UNK_ERROR);
|
||||
}
|
||||
}
|
||||
exit (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a string from stdin, returns a malloced copy of it
|
||||
*/
|
||||
static char *read_string(void)
|
||||
{
|
||||
char *buffer = NULL;
|
||||
char *check = NULL;
|
||||
int slen = 0;
|
||||
|
||||
buffer = g_malloc(BUFSIZ);
|
||||
if (buffer == NULL)
|
||||
return NULL;
|
||||
|
||||
check = fgets(buffer, BUFSIZ, stdin);
|
||||
if (!check)
|
||||
return NULL;
|
||||
slen = strlen(buffer);
|
||||
if((slen > 0) && ((buffer[slen - 1] == '\n') || isspace(buffer[slen - 1]))){
|
||||
buffer[slen-1] = '\0';
|
||||
}
|
||||
if(buffer[0] == UH_TEXT) {
|
||||
memmove(buffer, buffer + 1, BUFSIZ - 1);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/* Application data with some hints. */
|
||||
static struct app_data_t {
|
||||
int fallback;
|
||||
char *user;
|
||||
char *service;
|
||||
} app_data = {0, NULL, NULL};
|
||||
static gboolean fallback_flag = FALSE;
|
||||
|
||||
/*
|
||||
* Conversation function for the boring change password stuff
|
||||
*/
|
||||
static int conv_func(int num_msg, const struct pam_message **msg,
|
||||
struct pam_response **resp, void *appdata_ptr)
|
||||
{
|
||||
int count = 0;
|
||||
int responses = 0;
|
||||
struct pam_response *reply = NULL;
|
||||
char *noecho_message;
|
||||
|
||||
reply = (struct pam_response *)
|
||||
calloc(num_msg, sizeof(struct pam_response));
|
||||
if (reply == NULL)
|
||||
return PAM_CONV_ERR;
|
||||
|
||||
if(appdata_ptr != NULL) {
|
||||
struct app_data_t *app_data = (struct app_data_t*) appdata_ptr;
|
||||
|
||||
g_print("%d %d\n", UH_FALLBACK, app_data->fallback);
|
||||
if(app_data->user == NULL) {
|
||||
g_print("%d %s\n", UH_USER, "root");
|
||||
} else {
|
||||
g_print("%d %s\n", UH_USER, app_data->user);
|
||||
}
|
||||
if(app_data->service != NULL) {
|
||||
g_print("%d %s\n", UH_SERVICE_NAME, app_data->service);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We do first a pass on all items and output them;
|
||||
* then we do a second pass and read what we have to read
|
||||
* from stdin
|
||||
*/
|
||||
for (count = responses = 0; count < num_msg; count++) {
|
||||
switch (msg[count]->msg_style) {
|
||||
case PAM_PROMPT_ECHO_ON:
|
||||
g_print("%d %s\n", UH_ECHO_ON_PROMPT, msg[count]->msg);
|
||||
responses++;
|
||||
break;
|
||||
case PAM_PROMPT_ECHO_OFF:
|
||||
if (the_username && !strncasecmp(msg[count]->msg, "password", 8)) {
|
||||
noecho_message = g_strdup_printf(_("Password for %s"),
|
||||
the_username);
|
||||
} else {
|
||||
noecho_message = g_strdup(msg[count]->msg);
|
||||
}
|
||||
g_print("%d %s\n", UH_ECHO_OFF_PROMPT, noecho_message);
|
||||
g_free(noecho_message);
|
||||
responses++;
|
||||
break;
|
||||
case PAM_TEXT_INFO:
|
||||
g_print("%d %s\n", UH_INFO_MSG, msg[count]->msg);
|
||||
break;
|
||||
case PAM_ERROR_MSG:
|
||||
g_print("%d %s\n", UH_ERROR_MSG, msg[count]->msg);
|
||||
break;
|
||||
default:
|
||||
g_print("0 %s\n", msg[count]->msg);
|
||||
}
|
||||
}
|
||||
|
||||
/* tell the other side how many messages we expect responses for */
|
||||
g_print("%d %d\n", UH_EXPECT_RESP, responses);
|
||||
fflush(NULL);
|
||||
|
||||
/* now the second pass */
|
||||
for (count = 0; count < num_msg; count++) {
|
||||
switch (msg[count]->msg_style) {
|
||||
case PAM_TEXT_INFO:
|
||||
/* ignore it... */
|
||||
break;
|
||||
case PAM_ERROR_MSG:
|
||||
/* also ignore it... */
|
||||
break;
|
||||
case PAM_PROMPT_ECHO_ON:
|
||||
/* fall through */
|
||||
case PAM_PROMPT_ECHO_OFF:
|
||||
reply[count].resp_retcode = PAM_SUCCESS;
|
||||
reply[count].resp = read_string();
|
||||
if((reply[count].resp != NULL) &&
|
||||
(reply[count].resp[0] == UH_ABORT)) {
|
||||
fallback_flag = TRUE;
|
||||
free (reply);
|
||||
return PAM_MAXTRIES; /* Shrug. */
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Must be an error of some sort... */
|
||||
free (reply);
|
||||
return PAM_CONV_ERR;
|
||||
}
|
||||
}
|
||||
if (reply)
|
||||
*resp = reply;
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* the structure pointing at the conversation function for
|
||||
* auth and changing the password
|
||||
*/
|
||||
static struct pam_conv pipe_conv = {
|
||||
conv_func,
|
||||
&app_data,
|
||||
};
|
||||
static struct pam_conv text_conv = {
|
||||
misc_conv,
|
||||
&app_data,
|
||||
};
|
||||
|
||||
/*
|
||||
* A function to process already existing gecos information
|
||||
*/
|
||||
static void process_gecos(char *gecos)
|
||||
{
|
||||
char *idx;
|
||||
|
||||
if (gecos == NULL)
|
||||
return;
|
||||
|
||||
if (!full_name)
|
||||
full_name = gecos;
|
||||
idx = strchr(gecos, ',');
|
||||
if (idx != NULL) {
|
||||
*idx = '\0';
|
||||
gecos = idx+1;
|
||||
}
|
||||
if ((idx == NULL) || (*gecos == '\0')) {
|
||||
/* no more fields */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!office)
|
||||
office = gecos;
|
||||
idx = strchr(gecos, ',');
|
||||
if (idx != NULL) {
|
||||
*idx = '\0';
|
||||
gecos = idx+1;
|
||||
}
|
||||
|
||||
if (!office_ph)
|
||||
office_ph = gecos;
|
||||
idx = strchr(gecos, ',');
|
||||
if (idx != NULL) {
|
||||
*idx = '\0';
|
||||
gecos = idx+1;
|
||||
}
|
||||
if ((idx == NULL) || (*gecos == '\0')) {
|
||||
/* no more fields */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!home_ph)
|
||||
home_ph = gecos;
|
||||
idx = strchr(gecos, ',');
|
||||
if (idx != NULL) {
|
||||
*idx = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* invalid_field - insure that a field contains all legal characters
|
||||
*
|
||||
* The supplied field is scanned for non-printing and other illegal
|
||||
* characters. If any illegal characters are found, invalid_field
|
||||
* returns -1. Zero is returned for success.
|
||||
*/
|
||||
|
||||
int invalid_field(const char *field, const char *illegal)
|
||||
{
|
||||
const char *cp;
|
||||
|
||||
for (cp = field; *cp && isprint (*cp) && ! strchr (illegal, *cp); cp++)
|
||||
;
|
||||
if (*cp)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* A simple function to compute the gecos field size
|
||||
*/
|
||||
static int gecos_size(void)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
if (full_name != NULL)
|
||||
len += strlen(full_name);
|
||||
if (office != NULL)
|
||||
len += strlen(office);
|
||||
if (office_ph != NULL)
|
||||
len += strlen(office_ph);
|
||||
if (home_ph != NULL)
|
||||
len += strlen(home_ph);
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Snagged straight from the util-linux source... May want to clean up
|
||||
* a bit and possibly merge with the code in userinfo that parses to
|
||||
* get a list. -Otto
|
||||
*
|
||||
* get_shell_list () -- if the given shell appears in the list of valid shells,
|
||||
* return true. if not, return false.
|
||||
* if the given shell is NULL, the list of shells is outputted to stdout.
|
||||
*/
|
||||
static int get_shell_list(char* shell_name)
|
||||
{
|
||||
gboolean found;
|
||||
char *shell;
|
||||
|
||||
found = FALSE;
|
||||
setusershell();
|
||||
for(shell = getusershell(); shell != NULL; shell = getusershell()) {
|
||||
#ifdef DEBUG_USERHELPER
|
||||
fprintf(stderr, "got shell \"%s\"\n", shell);
|
||||
#endif
|
||||
if (shell_name) {
|
||||
if (! strcmp (shell_name, shell)) {
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else g_print("%s\n", shell);
|
||||
}
|
||||
endusershell();
|
||||
return found;
|
||||
}
|
||||
|
||||
#ifdef USE_MCHECK
|
||||
void
|
||||
mcheck_out(enum mcheck_status reason) {
|
||||
char *explanation;
|
||||
|
||||
switch (reason) {
|
||||
case MCHECK_DISABLED:
|
||||
explanation = _("Consistency checking is not turned on."); break;
|
||||
case MCHECK_OK:
|
||||
explanation = _("Block is fine."); break;
|
||||
case MCHECK_FREE:
|
||||
explanation = _("Block freed twice."); break;
|
||||
case MCHECK_HEAD:
|
||||
explanation = _("Memory before the block was clobbered."); break;
|
||||
case MCHECK_TAIL:
|
||||
explanation = _("Memory after the block was clobbered."); break;
|
||||
}
|
||||
g_print("%d %s\n", UH_ERROR_MSG, explanation);
|
||||
g_print("%d 1\n", UH_EXPECT_RESP);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ------- the application itself -------- */
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int arg;
|
||||
int retval;
|
||||
char *progname = NULL;
|
||||
pam_handle_t *pamh = NULL;
|
||||
struct passwd *pw;
|
||||
struct pam_conv *conv;
|
||||
int stdin_fileno = -1, stdout_fileno = -1, stderr_fileno = -1;
|
||||
int fd;
|
||||
FILE *fp;
|
||||
|
||||
#ifdef USE_MCHECK
|
||||
mtrace();
|
||||
mcheck(mcheck_out);
|
||||
#endif
|
||||
|
||||
/* first set up our locale info for gettext. */
|
||||
setlocale(LC_ALL, "");
|
||||
bindtextdomain (PACKAGE, GNOMELOCALEDIR);
|
||||
textdomain (PACKAGE);
|
||||
|
||||
if (geteuid() != 0) {
|
||||
fprintf(stderr, _("userhelper must be setuid root\n"));
|
||||
exit(ERR_NO_RIGHTS);
|
||||
}
|
||||
|
||||
if (argc < 2)
|
||||
g_error ("Usage: root-helper fd");
|
||||
|
||||
fd = atoi (argv[1]);
|
||||
if (fd <= STDERR_FILENO)
|
||||
g_error ("Usage: root-helper fd");
|
||||
|
||||
#ifdef DEBUG_USERHELPER
|
||||
fprintf (stderr, "fd is %d\n", fd);
|
||||
#endif
|
||||
fp = fdopen (fd, "r");
|
||||
|
||||
conv = &pipe_conv;
|
||||
|
||||
/* now try to identify the username we are doing all this work for */
|
||||
user_name = getlogin();
|
||||
if (user_name == NULL) {
|
||||
struct passwd *tmp;
|
||||
|
||||
tmp = getpwuid(getuid());
|
||||
if ((tmp != NULL) && (tmp->pw_name != NULL)) {
|
||||
user_name = g_strdup(tmp->pw_name);
|
||||
} else {
|
||||
/* weirdo, bail out */
|
||||
exit (ERR_UNK_ERROR);
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG_USERHELPER
|
||||
fprintf(stderr, "user is %s\n", user_name);
|
||||
#endif
|
||||
|
||||
{
|
||||
/* pick the first existing of /usr/sbin/<progname> and /sbin/<progname>
|
||||
* authenticate <progname>
|
||||
* argv[optind-1] = <progname> (boondoggle unless -- used)
|
||||
* (we know there is at least one open slot in argv for this)
|
||||
* execv(constructed_path, argv+optind);
|
||||
*/
|
||||
char *constructed_path;
|
||||
char *apps_filename;
|
||||
char *user, *apps_user;
|
||||
char *retry;
|
||||
char *env_home, *env_term, *env_display, *env_shell;
|
||||
char *env_user, *env_logname, *env_lang, *env_lcall, *env_lcmsgs;
|
||||
char *env_xauthority;
|
||||
int session, fallback, try;
|
||||
size_t aft;
|
||||
struct stat sbuf;
|
||||
shvarFile *s;
|
||||
|
||||
env_home = getenv("HOME");
|
||||
env_term = getenv("TERM");
|
||||
env_display = getenv("DISPLAY");
|
||||
env_shell = getenv("SHELL");
|
||||
env_user = getenv("USER");
|
||||
env_logname = getenv("LOGNAME");
|
||||
env_lang = getenv("LANG");
|
||||
env_lcall = getenv("LC_ALL");
|
||||
env_lcmsgs = getenv("LC_MESSAGES");
|
||||
env_xauthority = getenv("XAUTHORITY");
|
||||
|
||||
if (env_home && (strstr(env_home, "..") || strchr(env_home, '%')))
|
||||
env_home=NULL;
|
||||
if (env_shell && (strstr(env_shell, "..") || strchr(env_shell, '%')))
|
||||
env_shell=NULL;
|
||||
if (env_term && (strstr(env_term, "..") || strchr(env_term, '%')))
|
||||
env_term="dumb";
|
||||
if (env_lang && (strchr(env_lang, '/') || strchr(env_lang, '%')))
|
||||
env_lang=NULL;
|
||||
if (env_lcall && (strchr(env_lcall, '/') || strchr(env_lcall, '%')))
|
||||
env_lcall=NULL;
|
||||
if (env_lcmsgs && (strchr(env_lcmsgs, '/') || strchr(env_lcmsgs, '%')))
|
||||
env_lcmsgs=NULL;
|
||||
|
||||
environ = (char **) calloc (1, 2 * sizeof (char *));
|
||||
/* note that XAUTHORITY not copied -- do not let attackers get at
|
||||
* others' X authority records
|
||||
*/
|
||||
if (env_home) setenv("HOME", env_home, 1);
|
||||
if (env_term) setenv("TERM", env_term, 1);
|
||||
if (env_display) setenv("DISPLAY", env_display, 1);
|
||||
if (env_shell) setenv("SHELL", env_shell, 1);
|
||||
if (env_user) setenv("USER", env_user, 1);
|
||||
if (env_logname) setenv("LOGNAME", env_logname, 1);
|
||||
/* we want _, but only if it is safe */
|
||||
if (env_lang) setenv("LANG", env_lang, 1);
|
||||
if (env_lcall) setenv("LC_ALL", env_lcall, 1);
|
||||
if (env_lcmsgs) setenv("LC_MESSAGES", env_lcmsgs, 1);
|
||||
|
||||
setenv("PATH",
|
||||
"/usr/sbin:/usr/bin:/sbin:/bin:/usr/X11R6/bin:/root/bin", 1);
|
||||
|
||||
progname = "root-manager";
|
||||
user = "root";
|
||||
constructed_path = "cat";
|
||||
|
||||
retval = pam_start(progname, user, conv, &pamh);
|
||||
if (retval != PAM_SUCCESS)
|
||||
fail_error(retval);
|
||||
|
||||
session = TRUE;
|
||||
app_data.fallback = fallback = FALSE;
|
||||
app_data.user = user;
|
||||
app_data.service = progname;
|
||||
|
||||
do {
|
||||
#ifdef DEBUG_USERHELPER
|
||||
fprintf(stderr, _("PAM returned = %d\n"), retval);
|
||||
fprintf(stderr, _("about to authenticate \"%s\"\n"), user);
|
||||
#endif
|
||||
retval = pam_authenticate(pamh, 0);
|
||||
} while (--try && retval != PAM_SUCCESS && !fallback_flag);
|
||||
if (retval != PAM_SUCCESS) {
|
||||
pam_end(pamh, retval);
|
||||
if (fallback) {
|
||||
g_assert_not_reached ();
|
||||
setuid(getuid());
|
||||
if(geteuid() != getuid()) {
|
||||
exit (ERR_EXEC_FAILED);
|
||||
}
|
||||
argv[optind-1] = progname;
|
||||
if(d_flg) {
|
||||
dup2(stdin_fileno, STDIN_FILENO);
|
||||
dup2(stdout_fileno, STDOUT_FILENO);
|
||||
dup2(stderr_fileno, STDERR_FILENO);
|
||||
}
|
||||
execv(constructed_path, argv+optind-1);
|
||||
exit (ERR_EXEC_FAILED);
|
||||
} else {
|
||||
fail_error(retval);
|
||||
}
|
||||
}
|
||||
|
||||
retval = pam_acct_mgmt(pamh, 0);
|
||||
if (retval != PAM_SUCCESS) {
|
||||
pam_end(pamh, retval);
|
||||
fail_error(retval);
|
||||
}
|
||||
|
||||
/* reset the XAUTHORITY so that X stuff will work now */
|
||||
if (env_xauthority) setenv("XAUTHORITY", env_xauthority, 1);
|
||||
|
||||
if (session) {
|
||||
int child, status;
|
||||
|
||||
retval = pam_open_session(pamh, 0);
|
||||
if (retval != PAM_SUCCESS) {
|
||||
pam_end(pamh, retval);
|
||||
fail_error(retval);
|
||||
}
|
||||
|
||||
if ((child = fork()) == 0) {
|
||||
struct passwd *pw;
|
||||
setuid(0);
|
||||
pw = getpwuid(getuid());
|
||||
if (pw) setenv("HOME", pw->pw_dir, 1);
|
||||
|
||||
#define BUFSIZE 1024
|
||||
{
|
||||
char buf[BUFSIZE];
|
||||
#ifdef DEBUG_USERHELPER
|
||||
fprintf (stderr, "looping for input\n");
|
||||
#endif
|
||||
|
||||
while (fgets (buf, BUFSIZE, fp)) {
|
||||
if (!fork ()) {
|
||||
char **args;
|
||||
buf[strlen (buf) - 1] = '\0';
|
||||
args = g_strsplit (buf, " ", -1);
|
||||
#ifdef DEBUG_USERHELPER
|
||||
fprintf (stderr, "running: %s\n", args[0]);
|
||||
#endif
|
||||
|
||||
setenv("PATH",
|
||||
"/usr/sbin:/usr/bin:/sbin:/bin:/usr/X11R6/bin:/root/bin", 1);
|
||||
|
||||
execv (args[0], args);
|
||||
g_error ("s", g_strerror (errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
argv[optind-1] = progname;
|
||||
#ifdef DEBUG_USERHELPER
|
||||
g_print(_("about to exec \"%s\"\n"), constructed_path);
|
||||
#endif
|
||||
if(d_flg) {
|
||||
dup2(stdin_fileno, STDIN_FILENO);
|
||||
dup2(stdout_fileno, STDOUT_FILENO);
|
||||
dup2(stderr_fileno, STDERR_FILENO);
|
||||
}
|
||||
execv(constructed_path, argv+optind-1);
|
||||
exit (ERR_EXEC_FAILED);
|
||||
}
|
||||
|
||||
close(STDIN_FILENO);
|
||||
close(STDOUT_FILENO);
|
||||
close(STDERR_FILENO);
|
||||
|
||||
wait4 (child, &status, 0, NULL);
|
||||
|
||||
retval = pam_close_session(pamh, 0);
|
||||
if (retval != PAM_SUCCESS) {
|
||||
pam_end(pamh, retval);
|
||||
fail_error(retval);
|
||||
}
|
||||
|
||||
if (WIFEXITED(status) && (WEXITSTATUS(status) == 0)) {
|
||||
pam_end(pamh, PAM_SUCCESS);
|
||||
retval = 1;
|
||||
} else {
|
||||
pam_end(pamh, PAM_SUCCESS);
|
||||
retval = ERR_EXEC_FAILED;
|
||||
}
|
||||
exit (retval);
|
||||
|
||||
} else {
|
||||
/* this is not a session, so do not do session management */
|
||||
|
||||
pam_end(pamh, PAM_SUCCESS);
|
||||
|
||||
|
||||
#define BUFSIZE 1024
|
||||
{
|
||||
char buf[BUFSIZE];
|
||||
#ifdef DEBUG_USERHELPER
|
||||
fprintf (stderr, "looping for input\n");
|
||||
#endif
|
||||
|
||||
while (fgets (buf, BUFSIZE, fp)) {
|
||||
if (!fork ()) {
|
||||
char **args;
|
||||
buf[strlen (buf) - 1] = '\0';
|
||||
args = g_strsplit (buf, " ", -1);
|
||||
#ifdef DEBUG_USERHELPER
|
||||
fprintf (stderr, "running: %s\n", args[0]);
|
||||
#endif
|
||||
|
||||
setenv("PATH",
|
||||
"/usr/sbin:/usr/bin:/sbin:/bin:/usr/X11R6/bin:/root/bin", 1);
|
||||
|
||||
execv (args[0], args);
|
||||
g_error ("s", g_strerror (errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* time for an exec */
|
||||
setuid(0);
|
||||
argv[optind-1] = progname;
|
||||
#ifdef DEBUG_USERHELPER
|
||||
g_print(_("about to exec \"%s\"\n"), constructed_path);
|
||||
#endif
|
||||
if(d_flg) {
|
||||
dup2(stdin_fileno, STDIN_FILENO);
|
||||
dup2(stdout_fileno, STDOUT_FILENO);
|
||||
dup2(stderr_fileno, STDERR_FILENO);
|
||||
}
|
||||
execv(constructed_path, argv+optind-1);
|
||||
|
||||
exit (ERR_EXEC_FAILED);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* all done */
|
||||
if (pamh != NULL)
|
||||
retval = pam_end(pamh, PAM_SUCCESS);
|
||||
if (retval != PAM_SUCCESS)
|
||||
fail_error(retval);
|
||||
exit (0);
|
||||
}
|
53
root-manager/root-manager.h
Normal file
53
root-manager/root-manager.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/* Copyright (C) 1997-1999 Red Hat Software, Inc.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef __USERHELPER_H__
|
||||
#define __USERHELPER_H__
|
||||
|
||||
#define _(String) gettext(String)
|
||||
|
||||
#define UH_ECHO_ON_PROMPT 1
|
||||
#define UH_ECHO_OFF_PROMPT 2
|
||||
#define UH_INFO_MSG 3
|
||||
#define UH_ERROR_MSG 4
|
||||
#define UH_EXPECT_RESP 5
|
||||
#define UH_SERVICE_NAME 6
|
||||
#define UH_FALLBACK 7
|
||||
#define UH_USER 8
|
||||
|
||||
#define UH_TEXT 1
|
||||
#define UH_ABORT 2
|
||||
|
||||
#define ERR_PASSWD_INVALID 1 /* password is not right */
|
||||
#define ERR_FIELDS_INVALID 2 /* gecos fields invalid or
|
||||
* sum(lengths) too big */
|
||||
#define ERR_SET_PASSWORD 3 /* password resetting error */
|
||||
#define ERR_LOCKS 4 /* some files are locked */
|
||||
#define ERR_NO_USER 5 /* user unknown ... */
|
||||
#define ERR_NO_RIGHTS 6 /* insufficient rights */
|
||||
#define ERR_INVALID_CALL 7 /* invalid call to this program */
|
||||
#define ERR_SHELL_INVALID 8 /* invalid call to this program */
|
||||
#define ERR_NO_MEMORY 9 /* out of memory */
|
||||
#define ERR_NO_PROGRAM 10 /* -w progname not found */
|
||||
#define ERR_EXEC_FAILED 11 /* exec failed for some reason */
|
||||
#define ERR_UNK_ERROR 255 /* unknown error */
|
||||
|
||||
#define UH_PATH "/gnome/sbin/root-manager"
|
||||
#define UH_KEY_PIXMAP_PATH "/usr/share/pixmaps/userhelper-keys.xpm"
|
||||
/*#define UH_PATH "./root-manager"*/
|
||||
|
||||
#endif /* __USERHELPER_H__ */
|
377
root-manager/shvar.c
Normal file
377
root-manager/shvar.c
Normal file
|
@ -0,0 +1,377 @@
|
|||
/* copied from rp3 -- DO NOT EDIT HERE, ONLY COPY FROM rp3 */
|
||||
/*
|
||||
* shvar.c
|
||||
*
|
||||
* Implementation of non-destructively reading/writing files containing
|
||||
* only shell variable declarations and full-line comments.
|
||||
*
|
||||
* Includes explicit inheritance mechanism intended for use with
|
||||
* Red Hat Linux ifcfg-* files. There is no protection against
|
||||
* inheritance loops; they will generally cause stack overflows.
|
||||
* Furthermore, they are only intended for one level of inheritance;
|
||||
* the value setting algorithm assumes this.
|
||||
*
|
||||
* Copyright 1999 Red Hat, Inc.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "shvar.h"
|
||||
|
||||
/* Open the file <name>, return shvarFile on success, NULL on failure */
|
||||
shvarFile *
|
||||
svNewFile(char *name)
|
||||
{
|
||||
shvarFile *s = NULL;
|
||||
int closefd = 0;
|
||||
|
||||
s = calloc(sizeof(shvarFile), 1);
|
||||
if (!s) return NULL;
|
||||
|
||||
s->fd = open(name, O_RDWR); /* NOT O_CREAT */
|
||||
if (s->fd == -1) {
|
||||
/* try read-only */
|
||||
s->fd = open(name, O_RDONLY); /* NOT O_CREAT */
|
||||
if (s->fd) closefd = 1;
|
||||
}
|
||||
s->fileName = strdup(name);
|
||||
|
||||
if (s->fd != -1) {
|
||||
struct stat buf;
|
||||
char *tmp;
|
||||
|
||||
if (fstat(s->fd, &buf) < 0) goto bail;
|
||||
s->arena = calloc(buf.st_size, 1);
|
||||
if (!s->arena) goto bail;
|
||||
if (read(s->fd, s->arena, buf.st_size) < 0) goto bail;
|
||||
/* Yes, I know that strtok is evil, except that this is
|
||||
* precisely what it was intended for in the first place...
|
||||
*/
|
||||
tmp = strtok(s->arena, "\n");
|
||||
while (tmp) {
|
||||
s->lineList = g_list_append(s->lineList, tmp);
|
||||
tmp = strtok(NULL, "\n");
|
||||
}
|
||||
if (closefd) {
|
||||
close(s->fd);
|
||||
s->fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
|
||||
bail:
|
||||
if (s->fd != -1) close(s->fd);
|
||||
if (s->arena) free (s->arena);
|
||||
if (s->fileName) free (s->fileName);
|
||||
free (s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* remove escaped characters in place */
|
||||
static void
|
||||
unescape(char *s) {
|
||||
int len, i;
|
||||
|
||||
len = strlen(s);
|
||||
if ((s[0] == '"' || s[0] == '\'') && s[0] == s[len-1]) {
|
||||
i = len - 2;
|
||||
memmove(s, s+1, i);
|
||||
s[i+1] = '\0';
|
||||
len = i;
|
||||
}
|
||||
for (i = 0; i < len; i++) {
|
||||
if (s[i] == '\\') {
|
||||
memmove(s+i, s+i+1, len-(i+1));
|
||||
len--;
|
||||
}
|
||||
s[len] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* create a new string with all necessary characters escaped.
|
||||
* caller must free returned string
|
||||
*/
|
||||
static const char escapees[] = "\"'\\$~`"; /* must be escaped */
|
||||
static const char spaces[] = " \t"; /* only require "" */
|
||||
static char *
|
||||
escape(char *s) {
|
||||
char *new;
|
||||
int i, j, mangle = 0, space = 0;
|
||||
int newlen, slen;
|
||||
static int esclen, splen;
|
||||
|
||||
if (!esclen) esclen = strlen(escapees);
|
||||
if (!splen) splen = strlen(spaces);
|
||||
for (i = 0; i < esclen; i++) {
|
||||
if (strchr(s, escapees[i])) mangle++;
|
||||
}
|
||||
for (i = 0; i < splen; i++) {
|
||||
if (strchr(s, spaces[i])) space++;
|
||||
}
|
||||
if (!mangle && !space) return strdup(s);
|
||||
|
||||
slen = strlen(s);
|
||||
newlen = slen + mangle + 3; /* 3 is extra ""\0 */
|
||||
new = calloc(newlen, 1);
|
||||
if (!new) return NULL;
|
||||
|
||||
new[0] = '"';
|
||||
for (i = 0, j = 1; i < slen; i++, j++) {
|
||||
if (strchr(escapees, s[i])) {
|
||||
new[j++] = '\\';
|
||||
}
|
||||
new[j] = s[i];
|
||||
}
|
||||
new[j] = '"';
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
/* Get the value associated with the key, and leave the current pointer
|
||||
* pointing at the line containing the value. The char* returned MUST
|
||||
* be freed by the caller.
|
||||
*/
|
||||
char *
|
||||
svGetValue(shvarFile *s, char *key)
|
||||
{
|
||||
char *value = NULL;
|
||||
char *line;
|
||||
char *keyString;
|
||||
int len;
|
||||
|
||||
assert(s);
|
||||
assert(key);
|
||||
|
||||
keyString = calloc (strlen(key) + 2, 1);
|
||||
if (!keyString) return NULL;
|
||||
strcpy(keyString, key);
|
||||
keyString[strlen(key)] = '=';
|
||||
len = strlen(keyString);
|
||||
|
||||
for (s->current = s->lineList; s->current; s->current = s->current->next) {
|
||||
line = s->current->data;
|
||||
if (!strncmp(keyString, line, len)) {
|
||||
value = strdup(line + len);
|
||||
unescape(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(keyString);
|
||||
|
||||
if (value) {
|
||||
if (value[0]) {
|
||||
return value;
|
||||
} else {
|
||||
free (value);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if (s->parent) value = svGetValue(s->parent, key);
|
||||
return value;
|
||||
}
|
||||
|
||||
/* return 1 if <key> resolves to any truth value (e.g. "yes", "y", "true")
|
||||
* return 0 if <key> resolves to any non-truth value (e.g. "no", "n", "false")
|
||||
* return <default> otherwise
|
||||
*/
|
||||
int
|
||||
svTrueValue(shvarFile *s, char *key, int def)
|
||||
{
|
||||
char *tmp;
|
||||
int returnValue = def;
|
||||
|
||||
tmp = svGetValue(s, key);
|
||||
if (!tmp) return returnValue;
|
||||
|
||||
if ( (!strcasecmp("yes", tmp)) ||
|
||||
(!strcasecmp("true", tmp)) ||
|
||||
(!strcasecmp("t", tmp)) ||
|
||||
(!strcasecmp("y", tmp)) ) returnValue = 1;
|
||||
else
|
||||
if ( (!strcasecmp("no", tmp)) ||
|
||||
(!strcasecmp("false", tmp)) ||
|
||||
(!strcasecmp("f", tmp)) ||
|
||||
(!strcasecmp("n", tmp)) ) returnValue = 0;
|
||||
|
||||
free (tmp);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
|
||||
/* Set the variable <key> equal to the value <value>.
|
||||
* If <key> does not exist, and the <current> pointer is set, append
|
||||
* the key=value pair after that line. Otherwise, prepend the pair
|
||||
* to the top of the file. Here's the algorithm, as the C code
|
||||
* seems to be rather dense:
|
||||
*
|
||||
* if (value == NULL), then:
|
||||
* if val2 (parent): change line to key= or append line key=
|
||||
* if val1 (this) : delete line
|
||||
* else noop
|
||||
* else use this table:
|
||||
* val2
|
||||
* NULL value other
|
||||
* v NULL append line noop append line
|
||||
* a
|
||||
* l value noop noop noop
|
||||
* 1
|
||||
* other change line delete line change line
|
||||
*
|
||||
* No changes are ever made to the parent config file, only to the
|
||||
* specific file passed on the command line.
|
||||
*
|
||||
*/
|
||||
void
|
||||
svSetValue(shvarFile *s, char *key, char *value)
|
||||
{
|
||||
char *val1 = NULL, *val2 = NULL;
|
||||
char *keyValue;
|
||||
|
||||
assert(s);
|
||||
assert(key);
|
||||
/* value may be NULL */
|
||||
|
||||
if (value) value = escape(value);
|
||||
keyValue = g_malloc (strlen(key) + (value?strlen(value):0) + 2);
|
||||
if (!keyValue) return;
|
||||
sprintf(keyValue, "%s=%s", key, value?value:"");
|
||||
|
||||
val1 = svGetValue(s, key);
|
||||
if (val1 && value && !strcmp(val1, value)) goto bail;
|
||||
if (s->parent) val2 = svGetValue(s->parent, key);
|
||||
|
||||
if (!value) {
|
||||
/* delete value somehow */
|
||||
if (val2) {
|
||||
/* change/append line to get key= */
|
||||
if (s->current) s->current->data = keyValue;
|
||||
else s->lineList = g_list_append(s->lineList, keyValue);
|
||||
s->freeList = g_list_append(s->freeList, keyValue);
|
||||
s->modified = 1;
|
||||
} else if (val1) {
|
||||
/* delete line */
|
||||
s->lineList = g_list_remove_link(s->lineList, s->current);
|
||||
g_list_free_1(s->current);
|
||||
s->modified = 1;
|
||||
goto bail; /* do not need keyValue */
|
||||
}
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!val1) {
|
||||
if (val2 && !strcmp(val2, value)) goto end;
|
||||
/* append line */
|
||||
s->lineList = g_list_append(s->lineList, keyValue);
|
||||
s->freeList = g_list_append(s->freeList, keyValue);
|
||||
s->modified = 1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* deal with a whole line of noops */
|
||||
if (val1 && !strcmp(val1, value)) goto end;
|
||||
|
||||
/* At this point, val1 && val1 != value */
|
||||
if (val2 && !strcmp(val2, value)) {
|
||||
/* delete line */
|
||||
s->lineList = g_list_remove_link(s->lineList, s->current);
|
||||
g_list_free_1(s->current);
|
||||
s->modified = 1;
|
||||
goto bail; /* do not need keyValue */
|
||||
} else {
|
||||
/* change line */
|
||||
if (s->current) s->current->data = keyValue;
|
||||
else s->lineList = g_list_append(s->lineList, keyValue);
|
||||
s->freeList = g_list_append(s->freeList, keyValue);
|
||||
s->modified = 1;
|
||||
}
|
||||
|
||||
end:
|
||||
if (value) free(value);
|
||||
if (val1) free(val1);
|
||||
if (val2) free(val2);
|
||||
return;
|
||||
|
||||
bail:
|
||||
if (keyValue) free (keyValue);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Write the current contents iff modified. Returns -1 on error
|
||||
* and 0 on success. Do not write if no values have been modified.
|
||||
* The mode argument is only used if creating the file, not if
|
||||
* re-writing an existing file, and is passed unchanged to the
|
||||
* open() syscall.
|
||||
*/
|
||||
int
|
||||
svWriteFile(shvarFile *s, int mode)
|
||||
{
|
||||
FILE *f;
|
||||
int tmpfd;
|
||||
|
||||
if (s->modified) {
|
||||
if (s->fd == -1)
|
||||
s->fd = open(s->fileName, O_WRONLY|O_CREAT, mode);
|
||||
if (s->fd == -1)
|
||||
return -1;
|
||||
if (ftruncate(s->fd, 0) < 0)
|
||||
return -1;
|
||||
|
||||
tmpfd = dup(s->fd);
|
||||
f = fdopen(tmpfd, "w");
|
||||
fseek(f, 0, SEEK_SET);
|
||||
for (s->current = s->lineList; s->current; s->current = s->current->next) {
|
||||
char *line = s->current->data;
|
||||
fprintf(f, "%s\n", line);
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Close the file descriptor (if open) and delete the shvarFile.
|
||||
* Returns -1 on error and 0 on success.
|
||||
*/
|
||||
int
|
||||
svCloseFile(shvarFile *s)
|
||||
{
|
||||
|
||||
assert(s);
|
||||
|
||||
if (s->fd != -1) close(s->fd);
|
||||
|
||||
free(s->arena);
|
||||
for (s->current = s->freeList; s->current; s->current = s->current->next) {
|
||||
free(s->current->data);
|
||||
}
|
||||
free(s->fileName);
|
||||
g_list_free(s->freeList);
|
||||
g_list_free(s->lineList); /* implicitly frees s->current */
|
||||
free(s);
|
||||
return 0;
|
||||
}
|
100
root-manager/shvar.h
Normal file
100
root-manager/shvar.h
Normal file
|
@ -0,0 +1,100 @@
|
|||
/* copied from rp3 -- DO NOT EDIT HERE, ONLY COPY FROM rp3 */
|
||||
/*
|
||||
* shvar.h
|
||||
*
|
||||
* Interface for non-destructively reading/writing files containing
|
||||
* only shell variable declarations and full-line comments.
|
||||
*
|
||||
* Includes explicit inheritance mechanism intended for use with
|
||||
* Red Hat Linux ifcfg-* files. There is no protection against
|
||||
* inheritance loops; they will generally cause stack overflows.
|
||||
* Furthermore, they are only intended for one level of inheritance;
|
||||
* the value setting algorithm assumes this.
|
||||
*
|
||||
* Copyright 1999 Red Hat, Inc.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
#ifndef _SHVAR_H
|
||||
#define _SHVAR_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
typedef struct _shvarFile shvarFile;
|
||||
struct _shvarFile {
|
||||
char *fileName; /* read-only */
|
||||
int fd; /* read-only */
|
||||
char *arena; /* ignore */
|
||||
GList *lineList; /* read-only */
|
||||
GList *freeList; /* ignore */
|
||||
GList *current; /* set implicitly or explicitly,
|
||||
points to element of lineList */
|
||||
shvarFile *parent; /* set explicitly */
|
||||
int modified; /* ignore */
|
||||
};
|
||||
|
||||
|
||||
/* Open the file <name>, return shvarFile on success, NULL on failure */
|
||||
shvarFile *
|
||||
svNewFile(char *name);
|
||||
|
||||
/* Get the value associated with the key, and leave the current pointer
|
||||
* pointing at the line containing the value. The char* returned MUST
|
||||
* be freed by the caller.
|
||||
*/
|
||||
char *
|
||||
svGetValue(shvarFile *s, char *key);
|
||||
|
||||
/* return 1 if <key> resolves to any truth value (e.g. "yes", "y", "true")
|
||||
* return 0 if <key> resolves to any non-truth value (e.g. "no", "n", "false")
|
||||
* return <def> otherwise
|
||||
*/
|
||||
int
|
||||
svTrueValue(shvarFile *s, char *key, int def);
|
||||
|
||||
/* Set the variable <key> equal to the value <value>.
|
||||
* If <key> does not exist, and the <current> pointer is set, append
|
||||
* the key=value pair after that line. Otherwise, prepend the pair
|
||||
* to the top of the file.
|
||||
*/
|
||||
void
|
||||
svSetValue(shvarFile *s, char *key, char *value);
|
||||
|
||||
|
||||
/* Write the current contents iff modified. Returns -1 on error
|
||||
* and 0 on success. Do not write if no values have been modified.
|
||||
* The mode argument is only used if creating the file, not if
|
||||
* re-writing an existing file, and is passed unchanged to the
|
||||
* open() syscall.
|
||||
*/
|
||||
int
|
||||
svWriteFile(shvarFile *s, int mode);
|
||||
|
||||
/* Close the file descriptor (if open) and delete the shvarFile.
|
||||
* Returns -1 on error and 0 on success.
|
||||
*/
|
||||
int
|
||||
svCloseFile(shvarFile *s);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* ! _SHVAR_H */
|
229
root-manager/userdialogs.c
Normal file
229
root-manager/userdialogs.c
Normal file
|
@ -0,0 +1,229 @@
|
|||
/* -*-Mode: c-*- */
|
||||
/* Copyright (C) 1997 Red Hat Software, Inc.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#include <locale.h>
|
||||
#include <libintl.h>
|
||||
#define _(String) gettext(String)
|
||||
#include "userdialogs.h"
|
||||
|
||||
GtkWidget*
|
||||
create_message_box(gchar* message, gchar* title)
|
||||
{
|
||||
GtkWidget* message_box;
|
||||
GtkWidget* label;
|
||||
GtkWidget* hbox;
|
||||
GtkWidget* ok;
|
||||
|
||||
message_box = gtk_dialog_new();
|
||||
gtk_window_position(GTK_WINDOW(message_box), GTK_WIN_POS_CENTER);
|
||||
gtk_container_set_border_width(GTK_CONTAINER(message_box), 5);
|
||||
if(title == NULL)
|
||||
gtk_window_set_title(GTK_WINDOW(message_box), _("Message"));
|
||||
else
|
||||
gtk_window_set_title(GTK_WINDOW(message_box), title);
|
||||
|
||||
label = gtk_label_new(message);
|
||||
hbox = gtk_hbox_new(TRUE, 5);
|
||||
ok = gtk_button_new_with_label(_(UD_OK_TEXT));
|
||||
gtk_misc_set_padding(GTK_MISC(GTK_BIN(ok)->child), 4, 0);
|
||||
gtk_signal_connect_object(GTK_OBJECT(ok), "clicked",
|
||||
(GtkSignalFunc) gtk_widget_destroy,
|
||||
(gpointer) message_box);
|
||||
gtk_widget_set_usize(ok, 50, 0);
|
||||
|
||||
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(message_box)->vbox), hbox,
|
||||
FALSE, FALSE, 5);
|
||||
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
|
||||
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(message_box)->action_area), ok,
|
||||
FALSE, FALSE, 0);
|
||||
|
||||
GTK_WIDGET_SET_FLAGS(ok, GTK_CAN_DEFAULT);
|
||||
gtk_widget_grab_default(ok);
|
||||
|
||||
gtk_widget_show(ok);
|
||||
gtk_widget_show(label);
|
||||
gtk_widget_show(hbox);
|
||||
gtk_widget_show(message_box);
|
||||
|
||||
return message_box;
|
||||
|
||||
}
|
||||
|
||||
/* conslidate error_box and message_box.. they're the same damn thing
|
||||
* with a different default title.
|
||||
*/
|
||||
GtkWidget*
|
||||
create_error_box(gchar* error, gchar* title)
|
||||
{
|
||||
GtkWidget* error_box;
|
||||
GtkWidget* label;
|
||||
GtkWidget* hbox;
|
||||
GtkWidget* ok;
|
||||
|
||||
error_box = gtk_dialog_new();
|
||||
gtk_window_position(GTK_WINDOW(error_box), GTK_WIN_POS_CENTER);
|
||||
gtk_container_set_border_width(GTK_CONTAINER(error_box), 5);
|
||||
if(title == NULL)
|
||||
gtk_window_set_title(GTK_WINDOW(error_box), _("Error"));
|
||||
else
|
||||
gtk_window_set_title(GTK_WINDOW(error_box), title);
|
||||
|
||||
label = gtk_label_new(error);
|
||||
hbox = gtk_hbox_new(TRUE, 5);
|
||||
ok = gtk_button_new_with_label(_(UD_OK_TEXT));
|
||||
gtk_misc_set_padding(GTK_MISC(GTK_BIN(ok)->child), 4, 0);
|
||||
gtk_signal_connect_object(GTK_OBJECT(ok), "clicked",
|
||||
(GtkSignalFunc) gtk_widget_destroy,
|
||||
(gpointer) error_box);
|
||||
gtk_widget_set_usize(ok, 50, 0);
|
||||
|
||||
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(error_box)->vbox), hbox,
|
||||
FALSE, FALSE, 5);
|
||||
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
|
||||
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(error_box)->action_area), ok,
|
||||
FALSE, FALSE, 0);
|
||||
|
||||
GTK_WIDGET_SET_FLAGS(ok, GTK_CAN_DEFAULT);
|
||||
gtk_widget_grab_default(ok);
|
||||
|
||||
gtk_widget_show(ok);
|
||||
gtk_widget_show(label);
|
||||
gtk_widget_show(hbox);
|
||||
gtk_widget_show(error_box);
|
||||
|
||||
return error_box;
|
||||
}
|
||||
|
||||
GtkWidget*
|
||||
create_query_box(gchar* prompt, gchar* title, GtkSignalFunc func)
|
||||
{
|
||||
GtkWidget* query_box;
|
||||
GtkWidget* label;
|
||||
GtkWidget* entry;
|
||||
GtkWidget* hbox;
|
||||
GtkWidget* ok;
|
||||
|
||||
query_box = gtk_dialog_new();
|
||||
gtk_window_position(GTK_WINDOW(query_box), GTK_WIN_POS_CENTER);
|
||||
gtk_container_set_border_width(GTK_CONTAINER(query_box), 5);
|
||||
if(title == NULL)
|
||||
gtk_window_set_title(GTK_WINDOW(query_box), _("Prompt"));
|
||||
else
|
||||
gtk_window_set_title(GTK_WINDOW(query_box), _("Prompt"));
|
||||
|
||||
label = gtk_label_new(prompt);
|
||||
entry = gtk_entry_new();
|
||||
ok = gtk_button_new_with_label(_(UD_OK_TEXT));
|
||||
gtk_misc_set_padding(GTK_MISC(GTK_BIN(ok)->child), 4, 0);
|
||||
gtk_widget_set_usize(ok, 50, 0);
|
||||
|
||||
hbox = gtk_hbox_new(TRUE, 0);
|
||||
|
||||
gtk_signal_connect_object(GTK_OBJECT(entry), "activate",
|
||||
(GtkSignalFunc) gtk_button_clicked,
|
||||
(gpointer) GTK_BUTTON(ok));
|
||||
|
||||
/* FIXME: memory leak... well, not really. Just rely on the caller
|
||||
* to free the widget... 'cept that's not nice either. :-S
|
||||
*/
|
||||
gtk_signal_connect_object(GTK_OBJECT(ok), "clicked",
|
||||
(GtkSignalFunc) gtk_widget_hide,
|
||||
(gpointer) query_box);
|
||||
if(func != NULL)
|
||||
{
|
||||
gtk_signal_connect(GTK_OBJECT(ok), "clicked", func, entry);
|
||||
}
|
||||
|
||||
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(query_box)->vbox), label,
|
||||
FALSE, FALSE, 5);
|
||||
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(query_box)->vbox), hbox,
|
||||
FALSE, FALSE, 5);
|
||||
gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
|
||||
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(query_box)->action_area), ok,
|
||||
TRUE, FALSE, 0);
|
||||
|
||||
GTK_WIDGET_SET_FLAGS(ok, GTK_CAN_DEFAULT);
|
||||
gtk_widget_grab_default(ok);
|
||||
|
||||
gtk_widget_grab_focus(entry);
|
||||
|
||||
gtk_widget_show(ok);
|
||||
gtk_widget_show(label);
|
||||
gtk_widget_show(entry);
|
||||
gtk_widget_show(hbox);
|
||||
gtk_widget_show(query_box);
|
||||
|
||||
return query_box;
|
||||
}
|
||||
|
||||
GtkWidget*
|
||||
create_invisible_query_box(gchar* prompt, gchar* title, GtkSignalFunc func)
|
||||
{
|
||||
GtkWidget* query_box;
|
||||
GtkWidget* label;
|
||||
GtkWidget* entry;
|
||||
GtkWidget* hbox;
|
||||
GtkWidget* ok;
|
||||
|
||||
query_box = gtk_dialog_new();
|
||||
gtk_window_position(GTK_WINDOW(query_box), GTK_WIN_POS_CENTER);
|
||||
gtk_container_set_border_width(GTK_CONTAINER(query_box), 5);
|
||||
gtk_window_set_title(GTK_WINDOW(query_box), _("Prompt"));
|
||||
/* gtk_container_border_width(GTK_CONTAINER(GTK_DIALOG(query_box)->vbox), 5); */
|
||||
label = gtk_label_new(prompt);
|
||||
entry = gtk_entry_new();
|
||||
gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
|
||||
|
||||
hbox = gtk_hbox_new(TRUE, 5);
|
||||
|
||||
ok = gtk_button_new_with_label(_("OK"));
|
||||
gtk_misc_set_padding(GTK_MISC(GTK_BIN(ok)->child), 4, 0);
|
||||
|
||||
gtk_signal_connect_object(GTK_OBJECT(entry), "activate",
|
||||
(GtkSignalFunc) gtk_button_clicked,
|
||||
(gpointer) GTK_BUTTON(ok));
|
||||
gtk_signal_connect_object(GTK_OBJECT(ok), "clicked",
|
||||
(GtkSignalFunc) gtk_widget_hide,
|
||||
(gpointer) query_box);
|
||||
gtk_widget_set_usize(ok, 50, 0);
|
||||
|
||||
if(func != NULL)
|
||||
{
|
||||
gtk_signal_connect(GTK_OBJECT(ok), "clicked", func, entry);
|
||||
}
|
||||
|
||||
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(query_box)->vbox), label,
|
||||
FALSE, FALSE, 5);
|
||||
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(query_box)->vbox), hbox,
|
||||
FALSE, FALSE, 5);
|
||||
gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
|
||||
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(query_box)->action_area), ok,
|
||||
TRUE, FALSE, 0);
|
||||
|
||||
GTK_WIDGET_SET_FLAGS(ok, GTK_CAN_DEFAULT);
|
||||
gtk_widget_grab_default(ok);
|
||||
|
||||
gtk_widget_grab_focus(entry);
|
||||
|
||||
gtk_widget_show(ok);
|
||||
gtk_widget_show(label);
|
||||
gtk_widget_show(entry);
|
||||
gtk_widget_show(hbox);
|
||||
gtk_widget_show(query_box);
|
||||
|
||||
return query_box;
|
||||
}
|
42
root-manager/userdialogs.h
Normal file
42
root-manager/userdialogs.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/* -*-Mode: c-*- */
|
||||
/* Copyright (C) 1997 Red Hat Software, Inc.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef __USERDIALOGS_H__
|
||||
#define __USERDIALOGS_H__
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk/gdkx.h>
|
||||
#include <locale.h>
|
||||
#include <libintl.h>
|
||||
#define i18n(String) gettext(String)
|
||||
#define N_(String) String
|
||||
|
||||
#define UD_OK_TEXT N_("OK")
|
||||
#define UD_HELP_TEXT N_("Help")
|
||||
#define UD_CANCEL_TEXT N_("Cancel")
|
||||
#define UD_EXIT_TEXT N_("Exit")
|
||||
#define UD_FALLBACK_TEXT N_("Run Unprivileged")
|
||||
|
||||
/* consider a "has args" arg, so I can use the arg argument or not at will */
|
||||
GtkWidget* create_message_box(gchar* message, gchar* title);
|
||||
GtkWidget* create_error_box(gchar* error, gchar* title);
|
||||
GtkWidget* create_query_box(gchar* prompt, gchar* title, GtkSignalFunc func);
|
||||
GtkWidget* create_invisible_query_box(gchar* prompt, gchar* title,
|
||||
GtkSignalFunc func);
|
||||
|
||||
#endif /* __USERDIALOGS_H__ */
|
Loading…
Add table
Add a link
Reference in a new issue