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:
Jacob Berkman 2000-12-26 19:41:33 +00:00
parent 2749ba455b
commit d42affc965
73 changed files with 6567 additions and 3322 deletions

6
root-manager/.cvsignore Normal file
View file

@ -0,0 +1,6 @@
Makefile
Makefile.in
.libs
.deps
root-manager
root-manager-helper

26
root-manager/Makefile.am Normal file
View 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
View file

@ -0,0 +1 @@
1: write TODO file

View 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;
}

View 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));
}
}

View 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
View 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);
}

View 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
View 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
View 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
View 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;
}

View 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__ */