Skip to content

Commit

Permalink
networking changes for stack based multi-support (using existing fram…
Browse files Browse the repository at this point in the history
…ework)

additions for config
additions for sup-groups support
additions for uid/gid by name (in addition to number)
ringbuffer default size increased
lots of minor fixes
This commit contains memory leaks in network handling
  • Loading branch information
Jamie Beverly committed Dec 2, 2011
1 parent 1b3670d commit 87e6a0d
Show file tree
Hide file tree
Showing 23 changed files with 512 additions and 309 deletions.
70 changes: 45 additions & 25 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,66 @@
upkeeper 0.02
##########################

Prerequisites:
json-c: http://oss.metaparadigm.com/json-c/
sqlite: http://www.sqlite.org/
Summary:
Upkeeper is a process management framework, similar to systemd, DJB's
daemontools, runit, upstart, and others. What distinguishes upkeeper
from the others, is that it records changes in process state to a
data store, allowing you to more easily instrument monitoring,
alerting, metrics-collection, and automation.

Run

./configure
Installation:

and then
Prerequisites:
json-c: http://oss.metaparadigm.com/json-c/
sqlite: http://www.sqlite.org/

make
And for uptop:
ncurses: http://www.gnu.org/ncurses/

and optionally:
In the top directory,

make check
Run
./configure

and finally:
and then

make install
make

to compile this.
and optionally:

To run a quick demo, do
make check

cd demo
./demo-setup.sh
./upkeeper-init
and finally:

then open a new terminal with
make install

cd ..
cd uptop
./uptop
Using upkeeper:
Upkeeper consists of 3 logical parts, the 'controller', which manages
configuration and auditing; the 'buddy', which supervises tasks, and
ensures they remain running during the lifetime of buddy, as well as
reporting changes to the 'controller'; and 'clients', which are the
tools you use to interact or interogate the controller, add services,
etc.

(You can get out of the uptop screen by typing 'q').
In general, you will want to run the controller (upk_controller) as a
respawning service via init, or init-like system. Once controller is
running, you can add services to controller, remove services, query
configured services, interogate and otherwise list state histories,
etc.

Now, in the original terminal, in the 'demo' directory, type
The 'upk_config' utility is a client that is used to configure services.
'upk_list' lists configured services.
'upk_start' starts a given service.
'upk_stop' stops a given service.
'upk_ps' gives ps-like output for services.
'upk_top' is like upk_ps, but in an ncurses UI (like top.)
'upk_ctl' gets information from the controller.
'upk' is a wrapper around the above clients.





./demo-down.sh
./demo-up.sh

to watch the upkeeper dashboard update.

14 changes: 14 additions & 0 deletions autoscan.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
autoscan: warning: missing AC_PROG_CXX wanted by:
ltmain.sh:677
ltmain.sh:1015
ltmain.sh:1022
ltmain.sh:1022
ltmain.sh:1022
ltmain.sh:1022
ltmain.sh:1031
ltmain.sh:1037
ltmain.sh:1037
ltmain.sh:1037
ltmain.sh:1037
autoscan: warning: missing AC_PROG_RANLIB wanted by:
ltmain.sh:1601
Binary file modified clients/gmon.out
Binary file not shown.
8 changes: 8 additions & 0 deletions compat/strnlen.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
size_t
strnlen(const char * restrict s, size_t maxlen)
{
size_t n = 0;
while( n <= maxlen && *(s+n++) != '\0');

return n;
}
5 changes: 4 additions & 1 deletion configure
Original file line number Diff line number Diff line change
Expand Up @@ -15794,7 +15794,10 @@ fi



for ac_func in dup2 memset select socket strdup strerror memchr strtol strcasecmp atexit mkdir gettimeofday strtoul strncasecmp strchr strstr strnlen



for ac_func in dup2 memset select socket strdup strerror memchr strtol strcasecmp atexit mkdir gettimeofday strtoul strncasecmp strchr strstr strnlen rmdir initgroups setgroups
do
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
Expand Down
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ AC_FUNC_FORK
AC_FUNC_MALLOC
AC_FUNC_STRNLEN
AC_FUNC_REALLOC
AC_CHECK_FUNCS([dup2 memset select socket strdup strerror memchr strtol strcasecmp atexit mkdir gettimeofday strtoul strncasecmp strchr strstr strnlen])
AC_CHECK_FUNCS([dup2 memset select socket strdup strerror memchr strtol strcasecmp atexit mkdir gettimeofday strtoul strncasecmp strchr strstr strnlen rmdir initgroups setgroups])
AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK

## Check whether or not symbol '__progname' is defined
Expand Down
54 changes: 54 additions & 0 deletions configure.scan
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.

AC_PREREQ([2.63])
AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
AC_CONFIG_SRCDIR([t.c])
AC_CONFIG_HEADERS([config.h])

# Checks for programs.
AC_PROG_CXX
AC_PROG_AWK
AC_PROG_CC
AC_PROG_CPP
AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_MAKE_SET
AC_PROG_RANLIB

# Checks for libraries.

# Checks for header files.
AC_CHECK_HEADERS([arpa/inet.h fcntl.h float.h stddef.h stdint.h stdlib.h string.h sys/file.h sys/socket.h sys/time.h unistd.h])

# Checks for typedefs, structures, and compiler characteristics.
AC_HEADER_STDBOOL
AC_TYPE_UID_T
AC_C_INLINE
AC_TYPE_INT16_T
AC_TYPE_INT32_T
AC_TYPE_INT8_T
AC_TYPE_MODE_T
AC_TYPE_PID_T
AC_C_RESTRICT
AC_TYPE_SIZE_T
AC_TYPE_UINT16_T
AC_TYPE_UINT32_T
AC_TYPE_UINT8_T

# Checks for library functions.
AC_FUNC_FORK
AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK
AC_FUNC_REALLOC
AC_FUNC_STRNLEN
AC_CHECK_FUNCS([atexit gettimeofday memset mkdir rmdir select socket strcasecmp strchr strerror strncasecmp strstr strtoul])

AC_CONFIG_FILES([Makefile
clients/Makefile
controller/Makefile
docs/latex/Makefile
libupkeeper/Makefile
libupkeeper/tests/Makefile])
AC_CONFIG_SUBDIRS([deps/sqlite
deps/sqlite-3.7.3])
AC_OUTPUT
32 changes: 27 additions & 5 deletions controller/buddy.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ size_t ringbuffer_size = 32;
char **proc_envp = NULL;
long reconnect_retries = 5;
bool randomize_ratelimit = false;
bool initialize_supplemental_groups = false;
bool clear_supplemental_groups = false;
uint32_t user_ratelimit = 0;

void buddy_init(diag_output_callback_t logger);
Expand Down Expand Up @@ -521,9 +523,29 @@ buddy_build_fd_set(fd_set * socks, bool listen_sock)
return (buddy_sockfd > buddy_ctrlfd) ? buddy_sockfd : buddy_ctrlfd;
}

/*********************************************************************************************************************
@brief initgroups/setgroups support
*********************************************************************************************************************/
static inline void
buddy_supp_groups(void)
{
struct passwd * pw = getpwuid(buddy_setuid);

/* ********************************************************************************************************************
******************************************************************************************************************* */
#ifdef HAVE_SETGROUPS
if(clear_supplemental_groups)
setgroups(0, (const gid_t *) NULL);
#endif
if(pw) {
#ifdef HAVE_INITGROUPS
if(initialize_supplemental_groups && buddy_setuid)
initgroups(pw->pw_name,pw->pw_gid);
#endif
}
}


/*********************************************************************************************************************
*********************************************************************************************************************/
static pid_t
buddy_exec_action(void)
{
Expand Down Expand Up @@ -562,8 +584,8 @@ buddy_exec_action(void)

/* dynamic allocation is mostly safe here; if it fails, the exec will probably fail; discounting
ulimits */
// assert((pathp = calloc(1, strlen(path_buf) + 1)));
// strcpy(pathp, path_buf);

buddy_supp_groups();

buddy_setreguid();

Expand All @@ -583,7 +605,7 @@ buddy_exec_action(void)
service stopped, or perhaps more interesting things */

setsid();
setpgid(0, 0);
setpgid(0, 0); /* should have already happened via setsid */

/* TODO: Support cgroups on linux (i.e. use cgcreate/cgexec to be used similarly to the pgrp or sid
above, but in a way that cannot be abandoned by the monitored process; can then use the cgroup
Expand Down
71 changes: 34 additions & 37 deletions controller/buddy.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,29 @@
@mainpage Buddy
"buddy" is the servicer-restarter component of upkeeper. It has 3 primary design goals:
"buddy" is the service-restarter component of upkeeper. It has 3 primary design goals:
1) Reliability - a buddy should never abort or otherwise fail after having been properly initialized. It
is being relied upon to restart other services when they fail, and so what it does, and how it does it
must be kept as simple, and as safe as possible. For instance, there is no runtime heap allocation, and
runtime stack allocation is kept to a minimum, so even with restrictive ulimits, a buddy should not fail
after initialization.
1) Reliability - a buddy should never abort or otherwise fail after having been properly initialized. It is being relied upon to restart
other services when they fail, and so what it does, and how it does it must be kept as simple, and as safe as possible. For instance,
there is no runtime heap allocation, and runtime stack allocation is kept to a minimum, so even with restrictive ulimits, a buddy should
not fail after initialization.
2) Audit - buddy implements a fixed ringbuffer, which it uses to queue events it has monitored, and report
those back to the controller daemon if/when the controller is available. To that end, buddy will send the
entire-contents of its ringbuffer to a controller when it connects to buddy. However, two circumstances
exist where the buddy may need to actively connect the controller, rather than passively waiting for one
to come by; they are a) when the ringbuffer has exceeded 3/4 fullness and b) when a buddy is terminated in
a mannor other than a controller shutting it down.
2) Audit - buddy implements a fixed ringbuffer, which it uses to queue events it has monitored, and report those back to the controller
daemon if/when the controller is available. To that end, buddy will send the entire-contents of its ringbuffer to a controller when it
connects to buddy. However, two circumstances exist where the buddy may need to actively connect the controller, rather than passively
waiting for one to come by; they are a) when the ringbuffer has exceeded 3/4 fullness and b) when a buddy is terminated in a mannor other
than a controller shutting it down.
3) Flexibility - While buddy must remain somewhat limited in what it can do itself, it has been designed
specifically to remain flexible in how it is used, and amenable to the use of adjuncts to extend and
enhance its own functionality. In essence, buddy will avoid any behavior that might prevent you from
doing whatever you need to do.
3) Flexibility - While buddy must remain somewhat limited in what it can do itself, it has been designed specifically to remain flexible
in how it is used, and amenable to the use of adjuncts to extend and enhance its own functionality. In essence, buddy will avoid any
behavior that might prevent you from doing whatever you need to do.
Buddy operates like this:
* buddy starts
* buddy initializes its ringbuffer, some string buffers, and a creates a domain socket to listen for
controllers.
* buddy initializes its ringbuffer, some string buffers, and a creates a domain socket to listen for controllers.
* buddy registers a signal handler for CHLD which sets a flag to restart its managed process.
Expand All @@ -53,24 +49,21 @@
* sometime later, a controller connects to the buddy, and issues a command, eg, start, or runonce, etc.
* buddy performs the action associated with whatever command it received, and reports back to the
controller everything in its ringbuffer, the last item being the state of the buddy after completing the
last action (but not necessarily the result of that last action; the controller may not obtain a result
until some future poll)
* buddy performs the action associated with whatever command it received, and reports back to the controller everything in its ringbuffer,
the last item being the state of the buddy after completing the last action (but not necessarily the result of that last action; the
controller may not obtain a result until some future poll)
* the 'start' action is the only action that will be monitored. The script associated with the start
action is what buddy will monitor; hence, that script should usually 'exec /path/to/yourservice' after
performing any initialization you may require.
* the 'start' action is the only action that will be monitored. The script associated with the start action is what buddy will monitor;
hence, that script should usually 'exec /path/to/yourservice' after performing any initialization you may require.
* buddy supports up to 32 user-defined actions, which can be named anything you like, and can do anything
you want; They receive the pid (and subsequently, the pgrp and sid) of the managed process as their first
(and currently, only) argument. [note that buddy calls these user-defined actions by number, and not by
name, as it simplifies the protocol, and keeps buddy from having to perform (or potentially failing to
perform) bounds-checking for string operations]
* buddy supports up to 32 user-defined actions, which can be named anything you like, and can do anything you want; They receive the pid
(and subsequently, the pgrp and sid) of the managed process as their first (and currently, only) argument. [note that buddy calls these
user-defined actions by number, and not by name, as it simplifies the protocol, and keeps buddy from having to perform (or potentially
failing to perform) bounds-checking for string operations]
If a the monitored child dies during its lifetime, buddy will restart it. If that child is respawning too
quickly, it will be delayed, to help ensure the controller has time to collect information on the
failures, and to help prevent the respawn to negatively impact the system. */
If a the monitored child dies during its lifetime, buddy will restart it. If that child is respawning too quickly, it will be delayed, to
help ensure the controller has time to collect information on the failures, and to help prevent the respawn to negatively impact the
system. */



Expand All @@ -83,18 +76,22 @@
#define BUDDY_MAX_STRING_LEN UPK_MAX_STRING_LEN
#define BUDDY_MAX_PATH_LEN UPK_MAX_PATH_LEN

/* buddy.c */
extern upk_uuid_t buddy_uuid;
extern char *buddy_service_name;
extern char buddy_root_path[BUDDY_MAX_PATH_LEN];
extern int32_t verbosity;
extern char buddy_root_path[8192];
extern uid_t buddy_setuid;
extern gid_t buddy_setgid;
extern size_t ringbuffer_size;
extern long reconnect_retries;
extern char **proc_envp;
extern long reconnect_retries;
extern bool randomize_ratelimit;
extern bool initialize_supplemental_groups;
extern bool clear_supplemental_groups;
extern uint32_t user_ratelimit;
extern void buddy_init(diag_output_callback_t logger);
extern void commit_buddycide(int32_t signum);
extern int32_t buddy_event_loop(void);
extern void buddy_cleanup(void);


#endif
Loading

0 comments on commit 87e6a0d

Please sign in to comment.