/* Copyright (C) 2001-2005 Dmitry V. Levin Utempter library interface. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE # define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include "utempter.h" #ifndef TEMP_FAILURE_RETRY # define TEMP_FAILURE_RETRY(expression) \ (__extension__ \ ({ long int __result; \ do __result = (long int) (expression); \ while (__result == -1L && errno == EINTR); \ __result; })) #endif #define UTEMPTER_DEFAULT_PATHNAME LIBEXECDIR "/utempter/utempter" static const char *utempter_pathname; static int saved_fd = -1; static void __attribute__ ((__noreturn__)) do_child(int master_fd, const char *path, char *const *argv) { if ((dup2(master_fd, STDIN_FILENO) != STDIN_FILENO) || (dup2(master_fd, STDOUT_FILENO) != STDOUT_FILENO)) { #ifdef UTEMPTER_DEBUG fprintf(stderr, "libutempter: dup2: %s\n", strerror(errno)); #endif _exit(EXIT_FAILURE); } execv(path, argv); #ifdef UTEMPTER_DEBUG fprintf(stderr, "libutempter: execv: %s\n", strerror(errno)); #endif while (EACCES == errno) { /* try saved group ID */ gid_t rgid, egid, sgid; if (getresgid(&rgid, &egid, &sgid)) break; if (sgid == egid) break; if (setgid(sgid)) break; (void) execv(path, argv); break; } _exit(EXIT_FAILURE); } static int execute_helper(int master_fd, const char *const argv[]) { struct sigaction saved_action, action; action.sa_handler = SIG_DFL; action.sa_flags = SA_RESTART; sigemptyset(&action.sa_mask); if (sigaction(SIGCHLD, &action, &saved_action) < 0) { #ifdef UTEMPTER_DEBUG fprintf(stderr, "libutempter: sigaction: %s\n", strerror(errno)); #endif return 0; } else { pid_t child; int status = 1; child = fork(); if (!child) { do_child(master_fd, argv[0], (char *const *) argv); } else if (child < 0) { #ifdef UTEMPTER_DEBUG fprintf(stderr, "libutempter: fork: %s\n", strerror(errno)); #endif goto ret; } if (TEMP_FAILURE_RETRY(waitpid(child, &status, 0)) < 0) { #ifdef UTEMPTER_DEBUG fprintf(stderr, "libutempter: waitpid: %s\n", strerror(errno)); #endif status = 1; } ret: (void) sigaction(SIGCHLD, &saved_action, 0); return !status; } } /* New interface. */ int utempter_add_record(int master_fd, const char *hostname) { const char *const args[] = { utempter_pathname ? : UTEMPTER_DEFAULT_PATHNAME, "add", hostname, 0 }; int status = execute_helper(master_fd, args); if (status) saved_fd = master_fd; return status; } int utempter_remove_record(int master_fd) { const char *const args[] = { utempter_pathname ? : UTEMPTER_DEFAULT_PATHNAME, "del", 0 }; int status = execute_helper(master_fd, args); if (master_fd == saved_fd) saved_fd = -1; return status; } int utempter_remove_added_record(void) { if (saved_fd < 0) return 0; else return utempter_remove_record(saved_fd); } void utempter_set_helper(const char *pathname) { utempter_pathname = pathname; } /* Old interface. */ void addToUtmp(__attribute__ ((unused)) const char *pty, const char *hostname, int master_fd) { (void) utempter_add_record(master_fd, hostname); } void removeFromUtmp(void) { (void) utempter_remove_added_record(); } void removeLineFromUtmp(__attribute__ ((unused)) const char *pty, int master_fd) { (void) utempter_remove_record(master_fd); }