add -u and -g command line args to set user and group.

properly handle portability for mlockall and setrlimit
Tested on linux, Freebsd, solaris, mac.
FSCORE-47

git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@6033 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
Michael Jerris 2007-10-23 15:56:23 +00:00
parent 20380ea307
commit b382c0a41f
5 changed files with 238 additions and 8 deletions

View File

@ -170,7 +170,7 @@ AC_SUBST(DYNAMIC_LIB_EXTEN)
# Checks for header files. # Checks for header files.
AC_HEADER_DIRENT AC_HEADER_DIRENT
AC_HEADER_STDC AC_HEADER_STDC
AC_CHECK_HEADERS([sys/types.h]) AC_CHECK_HEADERS([sys/types.h sys/resource.h sched.h])
# Checks for typedefs, structures, and compiler characteristics. # Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST AC_C_CONST
@ -185,6 +185,59 @@ AC_FUNC_MALLOC
AC_TYPE_SIGNAL AC_TYPE_SIGNAL
AC_FUNC_STRFTIME AC_FUNC_STRFTIME
AC_CHECK_FUNCS([gethostname vasprintf mmap mlock mlockall usleep]) AC_CHECK_FUNCS([gethostname vasprintf mmap mlock mlockall usleep])
AC_CHECK_FUNCS([sched_setscheduler setpriority setrlimit setgroups initgroups])
AC_CHECK_DECL([RLIMIT_MEMLOCK],
[AC_DEFINE([HAVE_RLIMIT_MEMLOCK],[1],[RLIMIT_MEMLOCK constant for setrlimit])],,
[#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif])
AC_CHECK_DECL([SCHED_RR],
[AC_DEFINE([HAVE_SCHED_RR],[1],[SCHED_RR constant for sched_setscheduler])],,
[#ifdef HAVE_SCHED_H
#include <sched.h>
#endif])
#
# use mlockall only on linux (for now; if available)
#
if test "x${ac_cv_func_mlockall}" = "xyes"; then
AC_MSG_CHECKING([whether to use mlockall])
case "$host" in
*-linux-*)
AC_DEFINE([USE_MLOCKALL],[1],[Enable mlockall support])
AC_MSG_RESULT([yes])
USE_MLOCKALL=yes
;;
*-freebsd*)
AC_MSG_RESULT([no, broken for non-root users])
;;
*)
AC_MSG_RESULT([no])
;;
esac
#
# setrlimit prerequisites
#
if test "x${USE_MLOCKALL}" = "xyes" -a \
"x${ac_cv_func_setrlimit}" = "xyes" -a \
"x${ac_cv_have_decl_RLIMIT_MEMLOCK}" = "xyes"
then
AC_DEFINE([USE_SETRLIMIT],[1],[Use setrlimit to disable mlock limit for non-root users])
fi
fi
#
# sched_setcheduler + round-robin scheduler prerequisites
#
if test "x${ac_cv_func_sched_setscheduler}" = "xyes" -a \
"x${ac_cv_have_decl_SCHED_RR}" = "xyes"
then
AC_DEFINE([USE_SCHED_SETSCHEDULER],[1],[Enable round-robin scheduler using sched_setscheduler])
fi
AC_C_BIGENDIAN(AC_DEFINE([SWITCH_BYTE_ORDER],__BIG_ENDIAN,[Big Endian]),AC_DEFINE([SWITCH_BYTE_ORDER],__LITTLE_ENDIAN,[Little Endian])) AC_C_BIGENDIAN(AC_DEFINE([SWITCH_BYTE_ORDER],__BIG_ENDIAN,[Big Endian]),AC_DEFINE([SWITCH_BYTE_ORDER],__LITTLE_ENDIAN,[Little Endian]))

View File

@ -60,6 +60,19 @@ typedef apr_os_thread_t switch_thread_id_t;
#include <sys/mman.h> #include <sys/mman.h>
#endif #endif
#ifndef WIN32
/* setuid, setgid */
#include <unistd.h>
/* getgrnam, getpwnam */
#include <pwd.h>
#include <grp.h>
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif
#endif
/* #define DEBUG_ALLOC */ /* #define DEBUG_ALLOC */
#define DO_EVENTS #define DO_EVENTS

View File

@ -1413,6 +1413,18 @@ SWITCH_DECLARE(switch_status_t) switch_core_management_exec(char *relative_oid,
*/ */
SWITCH_DECLARE(int32_t) set_high_priority(void); SWITCH_DECLARE(int32_t) set_high_priority(void);
/*!
\brief Change user and/or group of the running process
\long Several possible combinations:
- user only (group NULL): switch to user and his primary group (and supplementary groups, if supported)
- user and group: switch to user and specified group (only)
- group only (user NULL): switch group only
\param user name of the user to switch to (or NULL)
\param group name of the group to switch to (or NULL)
\return 0 on success, -1 otherwise
*/
SWITCH_DECLARE(int32_t) change_user_group(const char *user, const char *group);
/*! /*!
\brief Run endlessly until the system is shutdown \brief Run endlessly until the system is shutdown
\param bg divert console to the background \param bg divert console to the background

View File

@ -205,6 +205,8 @@ int main(int argc, char *argv[])
const char *err = NULL; /* error value for return from freeswitch initialization */ const char *err = NULL; /* error value for return from freeswitch initialization */
#ifndef WIN32 #ifndef WIN32
int nf = 0; /* TRUE if we are running in nofork mode */ int nf = 0; /* TRUE if we are running in nofork mode */
char *runas_user = NULL;
char *runas_group = NULL;
#endif #endif
int nc = 0; /* TRUE if we are running in noconsole mode */ int nc = 0; /* TRUE if we are running in noconsole mode */
FILE *f; /* file handle to the pid file */ FILE *f; /* file handle to the pid file */
@ -214,6 +216,7 @@ int main(int argc, char *argv[])
char *usageDesc; char *usageDesc;
int alt_dirs = 0; int alt_dirs = 0;
int known_opt; int known_opt;
int high_prio = 0;
switch_core_flag_t flags = SCF_USE_SQL; switch_core_flag_t flags = SCF_USE_SQL;
#ifdef WIN32 #ifdef WIN32
@ -229,6 +232,8 @@ int main(int argc, char *argv[])
"\t-uninstall -- remove freeswitch as a service\n" "\t-uninstall -- remove freeswitch as a service\n"
#else #else
"\t-nf -- no forking\n" "\t-nf -- no forking\n"
"\t-u [user] -- specify user to switch to\n"
"\t-g [group] -- specify group to switch to\n"
#endif #endif
"\t-help -- this message\n" "\t-help -- this message\n"
"\t-hp -- enable high priority settings\n" "\t-hp -- enable high priority settings\n"
@ -280,6 +285,21 @@ int main(int argc, char *argv[])
} }
} }
#else #else
if (argv[x] && !strcmp(argv[x], "-u")) {
x++;
if (argv[x] && strlen(argv[x])) {
runas_user = argv[x];
}
known_opt++;
}
if (argv[x] && !strcmp(argv[x], "-g")) {
x++;
if (argv[x] && strlen(argv[x])) {
runas_group = argv[x];
}
known_opt++;
}
if (argv[x] && !strcmp(argv[x], "-nf")) { if (argv[x] && !strcmp(argv[x], "-nf")) {
nf++; nf++;
@ -287,7 +307,7 @@ int main(int argc, char *argv[])
} }
#endif #endif
if (argv[x] && !strcmp(argv[x], "-hp")) { if (argv[x] && !strcmp(argv[x], "-hp")) {
set_high_priority(); high_prio++;
known_opt++; known_opt++;
} }
@ -389,6 +409,19 @@ int main(int argc, char *argv[])
#endif #endif
} }
if (high_prio) {
set_high_priority();
}
#ifndef WIN32
if (runas_user || runas_group) {
if(change_user_group(runas_user, runas_group) < 0) {
fprintf(stderr, "Failed to switch user / group\n" );
return 255;
}
}
#endif
if (switch_core_init_and_modload(nc ? lfile : NULL, flags, &err) != SWITCH_STATUS_SUCCESS) { if (switch_core_init_and_modload(nc ? lfile : NULL, flags, &err) != SWITCH_STATUS_SUCCESS) {
fprintf(stderr, "Cannot Initilize [%s]\n", err); fprintf(stderr, "Cannot Initilize [%s]\n", err);
return 255; return 255;

View File

@ -360,7 +360,19 @@ SWITCH_DECLARE(void) switch_core_set_globals(void)
SWITCH_DECLARE(int32_t) set_high_priority(void) SWITCH_DECLARE(int32_t) set_high_priority(void)
{ {
#ifdef __linux__ #ifdef WIN32
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
#else
#ifdef USE_SETRLIMIT
struct rlimit lim = { RLIM_INFINITY, RLIM_INFINITY };
#endif
#ifdef USE_SCHED_SETSCHEDULER
/*
* Try to use a round-robin scheduler
* with a fallback if that does not work
*/
struct sched_param sched = { 0 }; struct sched_param sched = { 0 };
sched.sched_priority = 1; sched.sched_priority = 1;
if (sched_setscheduler(0, SCHED_RR, &sched)) { if (sched_setscheduler(0, SCHED_RR, &sched)) {
@ -371,19 +383,126 @@ SWITCH_DECLARE(int32_t) set_high_priority(void)
} }
#endif #endif
#ifdef WIN32 #ifdef HAVE_SETPRIORITY
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); /*
* setpriority() works on FreeBSD (6.2), nice() doesn't
*/
if (setpriority(PRIO_PROCESS, getpid(), -10) < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Could not set nice level\n");
}
#else #else
if(nice(-10)!= -10) { if (nice(-10) != -10) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Could not set nice level\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Could not set nice level\n");
} }
#endif #endif
#define USE_MLOCKALL #ifdef USE_SETRLIMIT
#ifdef HAVE_MLOCKALL /*
* The amount of memory which can be mlocked is limited for non-root users.
* FS will segfault (= hitting the limit) soon after mlockall has been called
* and we've switched to a different user.
* So let's try to remove the mlock limit here...
*/
if (setrlimit(RLIMIT_MEMLOCK, &lim) < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT,
"Failed to disable memlock limit, application may crash if run as non-root user!\n");
}
#endif
#ifdef USE_MLOCKALL #ifdef USE_MLOCKALL
/*
* Pin memory pages to RAM to prevent being swapped to disk
*/
mlockall(MCL_CURRENT | MCL_FUTURE); mlockall(MCL_CURRENT | MCL_FUTURE);
#endif #endif
#endif
return 0;
}
SWITCH_DECLARE(int32_t) change_user_group(const char *user, const char *group)
{
#ifndef WIN32
uid_t runas_uid = 0;
gid_t runas_gid = 0;
struct passwd *runas_pw = NULL;
if (user) {
/*
* Lookup user information in the system's db
*/
runas_pw = getpwnam( user );
if (!runas_pw) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Unknown user \"%s\"\n", user);
return -1;
}
runas_uid = runas_pw->pw_uid;
}
if (group) {
struct group *gr = NULL;
/*
* Lookup group information in the system's db
*/
gr = getgrnam( group );
if (!gr) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Unknown group \"%s\"\n", group);
return -1;
}
runas_gid = gr->gr_gid;
}
if (runas_uid) {
#ifdef HAVE_SETGROUPS
/*
* Drop all group memberships prior to changing anything
* or else we're going to inherit the parent's list of groups
* (which is not what we want...)
*/
if (setgroups(0, NULL) < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to drop group access list\n");
return -1;
}
#endif
if (runas_gid) {
/*
* A group has been passed, switch to it
* (without loading the user's other groups)
*/
if (setgid(runas_gid) < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to change gid!\n");
return -1;
}
} else {
/*
* No group has been passed, use the user's primary group in this case
*/
if (setgid(runas_pw->pw_gid) < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to change gid!\n");
return -1;
}
#ifdef HAVE_INITGROUPS
/*
* Set all the other groups the user is a member of
* (This can be really useful for fine-grained access control)
*/
if (initgroups(runas_pw->pw_name, runas_pw->pw_gid) < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to set group access list for user\n");
return -1;
}
#endif
}
/*
* Finally drop all privileges by switching to the new userid
*/
if (setuid(runas_uid) < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to change uid!\n");
return -1;
}
}
#endif #endif
return 0; return 0;
} }