├── NEWS ├── ChangeLog ├── Makefile.am ├── example ├── Makefile.am ├── erldapp.erl └── erldapp.init ├── bootstrap.sh ├── AUTHORS ├── .gitmodules ├── c_src ├── Makefile.am ├── slay.h ├── version.h ├── erld.h ├── pid.h ├── options.h ├── log.h ├── cnode.h ├── util.h ├── global.h ├── debug.h ├── pid.c ├── log.c ├── debug.c ├── util.c ├── slay.c ├── main.c ├── cnode.c └── erld.c ├── .gitignore ├── README.md ├── configure.ac ├── COPYING └── INSTALL /NEWS: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = c_src erld_erlang_app example 2 | -------------------------------------------------------------------------------- /example/Makefile.am: -------------------------------------------------------------------------------- 1 | dist_doc_DATA = erldapp.erl erldapp.init 2 | -------------------------------------------------------------------------------- /bootstrap.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | aclocal && autoconf -f && automake --foreign -a 3 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Originally written by Sam Bobroff 2 | Contributions from Bernard Duggan 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "erld_erlang_app"] 2 | path = erld_erlang_app 3 | url = https://github.com/ShoreTel-Inc/erld_erlang_app.git 4 | -------------------------------------------------------------------------------- /c_src/Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS = erld 2 | 3 | erld_SOURCES = cnode.c debug.c erld.c log.c main.c pid.c slay.c util.c cnode.h debug.h erld.h global.h log.h options.h pid.h slay.h util.h version.h 4 | 5 | erld_CFLAGS=-I$(ERLANG_LIB_DIR_erl_interface)/include -D_REENTRANT 6 | erld_LDADD=-L$(ERLANG_LIB_DIR_erl_interface)/lib -lei -lerl_interface -lpthread 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # General Ignorable Generated things 2 | Makefile 3 | Makefile.in 4 | aclocal.m4 5 | autom4te.* 6 | c_src/.deps/* 7 | c_src/Makefile 8 | c_src/Makefile.in 9 | compile 10 | config.log 11 | config.status 12 | configure 13 | depcomp 14 | install-sh 15 | missing 16 | src/Makefile 17 | src/Makefile.in 18 | 19 | # Compiled Object files 20 | *.slo 21 | *.lo 22 | *.o 23 | *.beam 24 | c_src/erld 25 | 26 | # Compiled Dynamic libraries 27 | *.so 28 | 29 | # Compiled Static libraries 30 | *.lai 31 | *.la 32 | *.a 33 | 34 | # Various temp files 35 | *~ 36 | *.swp 37 | *.orig 38 | -------------------------------------------------------------------------------- /c_src/slay.h: -------------------------------------------------------------------------------- 1 | /* 2 | erld - A UNIX-style daemon wrapper for Erlang 3 | Copyright (C) 2012 ShoreTel Inc. 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | void slay(pid_t pid); 21 | -------------------------------------------------------------------------------- /c_src/version.h: -------------------------------------------------------------------------------- 1 | /* 2 | erld - A UNIX-style daemon wrapper for Erlang 3 | Copyright (C) 2012 ShoreTel Inc. 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | #define VERSION_STRING "0.1" 21 | -------------------------------------------------------------------------------- /c_src/erld.h: -------------------------------------------------------------------------------- 1 | /* 2 | erld - A UNIX-style daemon wrapper for Erlang 3 | Copyright (C) 2012 ShoreTel Inc. 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | extern char *node_name; 21 | 22 | int erld_main(char *cookie, char *const argv[]); 23 | -------------------------------------------------------------------------------- /c_src/pid.h: -------------------------------------------------------------------------------- 1 | /* 2 | erld - A UNIX-style daemon wrapper for Erlang 3 | Copyright (C) 2012 ShoreTel Inc. 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | extern char *pid_file_name; 21 | 22 | void write_pid_file(); 23 | void remove_pid_file(); 24 | void pid_cleanup(); 25 | -------------------------------------------------------------------------------- /c_src/options.h: -------------------------------------------------------------------------------- 1 | /* 2 | erld - A UNIX-style daemon wrapper for Erlang 3 | Copyright (C) 2012 ShoreTel Inc. 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | extern int foreground; 21 | extern struct timeval heartbeat_timeout; 22 | extern struct timeval heartbeat_warn; 23 | extern int restart_limit; 24 | extern struct timeval restart_interval; 25 | extern time_t grace_period; 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ErlD 2 | ==== 3 | 4 | See the [github wiki](https://github.com/ShoreTel-Inc/erld/wiki) for 5 | documentation. You can also check out the example code under the 6 | `example` directory and run `erld -h` for an explanation of 7 | parameters. 8 | 9 | To build and install from a clean checkout: 10 | 11 | ./bootstrap.sh 12 | ./configure 13 | make 14 | sudo make install 15 | 16 | REQUIREMENTS 17 | ------------ 18 | 19 | erld doesn't require much to build. In addition to Erlang, you will 20 | need libproc and its development files (headers etc) and that should 21 | be all (aside from standard stuff like a compiler and autotools which 22 | pretty much any developer will already have installed). 23 | 24 | For Debian-derived systems, the development packages required are 25 | libprocps-dev and erlang-dev (or esl-erlang if you're using Erlang 26 | Solutions' packages). 27 | 28 | There are no specific version restrictions on Erlang that we're aware 29 | of - we've been running this code since R13 and are still using it 30 | without signficant modification on R16. If you do encounter a problem 31 | with a particular version, please let us know. 32 | 33 | 34 | -------------------------------------------------------------------------------- /c_src/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | erld - A UNIX-style daemon wrapper for Erlang 3 | Copyright (C) 2012 ShoreTel Inc. 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | /* See source file for description */ 21 | 22 | extern char *log_file_name; 23 | extern char *log_rotation_module; 24 | extern char *log_rotation_function; 25 | 26 | void setup_log_rotation(int pipe); 27 | void handle_rotate_signal(int arg); 28 | void rotate_log(); 29 | void log_open(); 30 | void log_write(const char *buf, size_t l); 31 | void log_cleanup(); 32 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT(UNIX daemon wrapper for Erlang, 1.0.1, bernie@m5.net, erld) 2 | AC_PREREQ(2.59c) 3 | AC_COPYRIGHT(Copyright (C) 2012 ShoreTel Inc.) 4 | 5 | 6 | AM_INIT_AUTOMAKE 7 | 8 | CFLAGS=" $CFLAGS -g -D__RELFILE__=__FILE__ -D_BSD_SOURCE" 9 | 10 | LDFLAGS=" $LDFLAGS -g" 11 | 12 | # Checks for programs. 13 | AC_PROG_CC 14 | AC_PROG_INSTALL 15 | AM_PROG_CC_C_O 16 | 17 | # Checks for header files. 18 | AC_STDC_HEADERS 19 | AC_CHECK_HEADERS([proc/readproc.h]) 20 | 21 | AC_SEARCH_LIBS([readproctab], [proc procps], , AC_MSG_ERROR([Unable to find readproctab(). Make sure either libproc or libprocps is installed])) 22 | 23 | AC_ERLANG_CHECK_LIB(erl_interface) 24 | AC_ERLANG_SUBST_INSTALL_LIB_SUBDIR(AC_PACKAGE_TARNAME, AC_PACKAGE_VERSION) 25 | AC_SUBST([ERLCFLAGS], [$erlcflags]) 26 | 27 | # Checks for typedefs, structures, and compiler characteristics. 28 | AC_CONFIG_FILES([ \ 29 | Makefile \ 30 | c_src/Makefile \ 31 | erld_erlang_app/Makefile \ 32 | example/Makefile \ 33 | ]) 34 | 35 | AC_OUTPUT 36 | echo 37 | echo $PACKAGE $VERSION 38 | echo 39 | eval eval echo erld will be installed in $bindir. 40 | echo 41 | echo configure complete, now type 'make' and then 'make install' 42 | echo 43 | -------------------------------------------------------------------------------- /c_src/cnode.h: -------------------------------------------------------------------------------- 1 | /* 2 | erld - A UNIX-style daemon wrapper for Erlang 3 | Copyright (C) 2012 ShoreTel Inc. 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | #include 21 | #include 22 | 23 | #define CNODE_ERROR -1 24 | #define CNODE_IGNORE 0 25 | #define CNODE_DETACH 1 26 | #define CNODE_THUMP 2 27 | #define CNODE_STR 3 28 | 29 | int start_cnode(const char *cookie, ei_cnode *ec, int *epmd_sock, int *listen_sock, const char *reg_name, const char *host_name); 30 | int accept_erlang_connection(ei_cnode *ec, int listen_sock, ErlConnect *onode); 31 | int cnode_read(int fd, char **ret_str); 32 | -------------------------------------------------------------------------------- /c_src/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | erld - A UNIX-style daemon wrapper for Erlang 3 | Copyright (C) 2012 ShoreTel Inc. 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | #include /* pid_t */ 21 | #include /* ... */ 22 | 23 | #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) 24 | #define MAX(X, Y) (((X) > (Y)) ? (X) : (Y)) 25 | 26 | char *alloc_vprintf(const char *fmt, va_list ap); 27 | char *alloc_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); 28 | void set_cleanup_handler(void(*handler)(int)); 29 | void set_signal(int signum, void (*handler)(int), int flags); 30 | int cleanup_child(int sig, const char *child_name, pid_t child); 31 | void get_exit_status(int status, char **exit_status, int *rv); 32 | -------------------------------------------------------------------------------- /c_src/global.h: -------------------------------------------------------------------------------- 1 | /* 2 | erld - A UNIX-style daemon wrapper for Erlang 3 | Copyright (C) 2012 ShoreTel Inc. 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | /* Global constants and defaults */ 21 | 22 | #define NAME "erld" /* name of the package, base for several other defaults and messages */ 23 | #define DEFAULT_LOG_FILE ("/var/log/" NAME ".log") 24 | #define DEFAULT_PID_FILE ("/var/run/" NAME ".pid") 25 | #define DEFAULT_COOKIE_FILE_NAME ".erlang.cookie" 26 | #define COOKIE_MAX_SIZE 128 27 | #define ERLD_MAX_TRIES 30 28 | #define ERLD_POLL_TIME 2000 /* milli-seconds */ 29 | #define RESTART_DELAY 5 /* milli-seconds to wait before restarting after a heartbeat timeout */ 30 | #define INITSCRIPT_EXIT 123 /* exit value used by init script to indicate clean exit */ 31 | #define DEFAULT_GRACE_PERIOD 5 /* seconds to wait after TSTOP before resorting to KILL */ 32 | #define DEFAULT_LOG_ROTATION_MODULE NAME 33 | #define DEFAULT_LOG_ROTATION_FUNCTION "rotate_logs" 34 | 35 | #define SIG_ROTATE_LOG 1 36 | #define SIG_WAKE 2 37 | -------------------------------------------------------------------------------- /c_src/debug.h: -------------------------------------------------------------------------------- 1 | /* 2 | erld - A UNIX-style daemon wrapper for Erlang 3 | Copyright (C) 2012 ShoreTel Inc. 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | /* See source file for description */ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | extern int debug_flag; 27 | extern int ei_tracelevel; // Hook into the ei library debug 28 | 29 | #define CHECK(STMT) if ((STMT) == -1) { fatal_error(__RELFILE__, __LINE__, errno, "%s", #STMT); } 30 | #define CHECK_F(STMT, FMT, ...) if ((STMT) == -1) { fatal_error(__RELFILE__, __LINE__, errno, FMT, __VA_ARGS__); } 31 | #define FATAL_ERROR(ERRNO, FMT) fatal_error(__RELFILE__, __LINE__, ERRNO, FMT) 32 | #define FATAL_ERROR_V(ERRNO, FMT, ...) fatal_error(__RELFILE__, __LINE__, ERRNO, FMT, __VA_ARGS__) 33 | 34 | #define DEBUG(FMT) debug(__FUNCTION__, FMT) 35 | #define DEBUG_V(FMT, ...) debug(__FUNCTION__, FMT, __VA_ARGS__) 36 | 37 | #define LOG(FMT) log_message(1, 1, __FUNCTION__, FMT) 38 | #define LOG_V(FMT, ...) log_message(1, 1, __FUNCTION__, FMT, __VA_ARGS__) 39 | 40 | void fatal_error(const char *file, int line, int e, const char *fmt, ...) __attribute__((format(printf, 4, 5))); 41 | void log_message(int prefix, int suffix, const char *fun, const char *fmt, ...) __attribute__((format(printf, 4, 5))); 42 | void debug(const char *fun, const char *fmt, ...) __attribute__((format(printf, 2, 3))); 43 | -------------------------------------------------------------------------------- /c_src/pid.c: -------------------------------------------------------------------------------- 1 | /* 2 | erld - A UNIX-style daemon wrapper for Erlang 3 | Copyright (C) 2012 ShoreTel Inc. 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | /* Pid file handling */ 21 | 22 | #include /* open, kill */ 23 | #include /* open */ 24 | #include /* open */ 25 | #include /* exit */ 26 | #include /* printf */ 27 | #include /* errno */ 28 | #include /* read, close, unlink, unistd */ 29 | #include /* kill */ 30 | #include /* strlen */ 31 | 32 | #include "global.h" 33 | #include "debug.h" 34 | 35 | char *pid_file_name; 36 | 37 | void write_pid_file() { 38 | int fd, l, rv; 39 | pid_t pid = -1; 40 | char buf[128]; 41 | const char *tmp_pid_file_name; 42 | 43 | /* look for an existing pid file */ 44 | tmp_pid_file_name = pid_file_name ? pid_file_name : DEFAULT_PID_FILE; 45 | fd = open(tmp_pid_file_name, O_RDONLY); 46 | if ((fd < 0) && (errno != ENOENT)) { 47 | perror("open"); 48 | exit(1); 49 | } 50 | if (fd >= 0) { 51 | l = read(fd, buf, (sizeof buf) - 1); 52 | if (l > 0) { 53 | buf[l] = 0; 54 | pid = atol(buf); 55 | rv = kill(pid, 0); 56 | if (!rv || (errno == EPERM)) { 57 | fprintf(stderr, "Already running on pid %u\n", pid); 58 | exit(1); 59 | } 60 | /* printf("rv = %d, errno = %d\n", rv, errno); */ 61 | } 62 | close(fd); 63 | fprintf(stderr, "Removing stale pid file (old pid %u).\n", pid); 64 | CHECK(unlink(tmp_pid_file_name)); 65 | } 66 | 67 | /* write a new pid file */ 68 | CHECK_F(fd = open(tmp_pid_file_name, O_WRONLY | O_CREAT | O_EXCL, 0666), "open(%s)", tmp_pid_file_name); 69 | l = snprintf(buf, sizeof buf, "%d\n", getpid()); 70 | write(fd, buf, l); 71 | close(fd); 72 | } 73 | 74 | void remove_pid_file() { 75 | unlink(pid_file_name ? pid_file_name : DEFAULT_PID_FILE); 76 | } 77 | 78 | void pid_cleanup() { 79 | if (pid_file_name) 80 | free(pid_file_name); 81 | } 82 | -------------------------------------------------------------------------------- /example/erldapp.erl: -------------------------------------------------------------------------------- 1 | % The MIT License (MIT) 2 | % Copyright (C) 2012 ShoreTel Inc. 3 | % 4 | % Permission is hereby granted, free of charge, to any person obtaining a copy 5 | % of this software and associated documentation files (the "Software"), to deal 6 | % in the Software without restriction, including without limitation the rights 7 | % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | % copies of the Software, and to permit persons to whom the Software is 9 | % furnished to do so, subject to the following conditions: 10 | % 11 | % The above copyright notice and this permission notice shall be included in 12 | % all copies or substantial portions of the Software. 13 | % 14 | % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | % THE SOFTWARE. 21 | 22 | % This module provides a very simple example of how to hook your app up to erld. 23 | 24 | -module(erldapp). 25 | -behaviour(application). 26 | -behaviour(erld_app). 27 | -behaviour(supervisor). 28 | 29 | % application callbacks 30 | -export([start/2, stop/1]). 31 | 32 | % supervisor callback 33 | -export([init/1]). 34 | 35 | % erld_app callbacks 36 | -export([bake_cookie/0, stop/0]). 37 | 38 | % Standard app start function 39 | start(_, _) -> 40 | % Set up our cookie to match the one erld expects 41 | erlang:set_cookie(node(), bake_cookie()), 42 | 43 | % Do your normal application startup here - if any of this fails, erld will exit 44 | % to the console with an error code 45 | {ok, AppPid} = supervisor:start_link({local, erldapp_sup}, ?MODULE, []), 46 | 47 | % This call will detach the erlang VM and return to the console with success (code 0). 48 | erld:detach(), 49 | 50 | % Do any startup that you want to fail in daemon mode here (that's probably none) 51 | 52 | {ok, AppPid}. 53 | 54 | % Standard app stop function 55 | stop(_) -> 56 | ok. 57 | 58 | % This function returns the cookie that this node will use. If you want some magic, randomly 59 | % generated cookie to be used for each iteration, or something else, generate it here. 60 | bake_cookie() -> 61 | 'superSecretCookie'. 62 | 63 | % This function is called by erld to conduct a graceful shutdown of the application 64 | % You can add your own cleanup code as required 65 | stop() -> 66 | application:stop(erldapp). 67 | 68 | % Top level supervisor 69 | init(_) -> 70 | {ok, {{one_for_one, 5, 5}, [ 71 | % The heartbeat process allows erld to detect if this erlang VM dies or locks up 72 | % or generally becomes unresponsive. 73 | erld_heartbeat:erld_heartbeat_spec() 74 | % Your other processes and nested supervisors go here 75 | ]}}. 76 | -------------------------------------------------------------------------------- /c_src/log.c: -------------------------------------------------------------------------------- 1 | /* 2 | erld - A UNIX-style daemon wrapper for Erlang 3 | Copyright (C) 2012 ShoreTel Inc. 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | /* Log file handling */ 21 | 22 | #define _XOPEN_SOURCE 500 /* strdup */ 23 | #define _LARGEFILE64_SOURCE /* the log file may grow large */ 24 | 25 | #include /* close */ 26 | #include /* O_ */ 27 | #include /* signal */ 28 | #include /* syslog */ 29 | #include /* free */ 30 | #include /* strdup */ 31 | 32 | #include "global.h" 33 | #include "debug.h" 34 | #include "util.h" 35 | #include "log.h" 36 | 37 | // All files are 'large' on mac os, so O_LARGEFILE is not defined - It's a linux extension. 38 | #ifdef __APPLE__ 39 | #ifndef O_LARGEFILE 40 | #define O_LARGEFILE 0 41 | #endif 42 | #endif 43 | 44 | 45 | char *log_file_name = 0; 46 | char *log_rotation_module = 0; 47 | char *log_rotation_function = 0; 48 | 49 | static int log_file_fd = -1; 50 | 51 | static int sig_pipe = 0; 52 | 53 | void setup_log_rotation(int pipe) { 54 | sig_pipe = pipe; 55 | set_signal(SIGHUP, handle_rotate_signal, SA_RESTART); 56 | } 57 | 58 | void handle_rotate_signal(int arg __attribute__((unused))) { 59 | char c[2] = {SIG_ROTATE_LOG, 0}; 60 | write(sig_pipe, &c, 1); 61 | } 62 | 63 | void log_open() { 64 | const char *tmp_log_file_name = log_file_name ? log_file_name : DEFAULT_LOG_FILE; 65 | DEBUG_V("Using log file %s", tmp_log_file_name); 66 | CHECK_F(log_file_fd = open(tmp_log_file_name, O_WRONLY | O_CREAT | O_APPEND | O_LARGEFILE, 0644), "open(%s)", tmp_log_file_name); 67 | } 68 | 69 | void rotate_log() { 70 | if (log_file_fd != -1) { 71 | DEBUG("Log file reopened"); 72 | syslog(LOG_INFO, "log file reopened"); 73 | close(log_file_fd); 74 | } 75 | log_open(); 76 | } 77 | 78 | /* NOTE: this is called during debug messages, so don't call debug from here! */ 79 | void log_write(const char *buf, size_t l) { 80 | if (log_file_fd == -1) 81 | write(2, buf, l); 82 | else 83 | write(log_file_fd, buf, l); 84 | /* It's tempting to add a call to fsync() here so that "logs never get 85 | lost". HOWEVER, doing so causes the main (and only) thread of erld 86 | to block until fsync() returns, which can be a LONG time (e.g. several 87 | seconds or more). Although erld's main loop checks for a heartbeat message 88 | before it checks for a timeout, it's possible that there are two messages 89 | in the pipe, and the first is not the heartbeat, which will allow the 90 | timeout to happen, causing an erroneous heartbeat timeout! */ 91 | } 92 | 93 | void log_cleanup() { 94 | if (log_file_fd != -1) 95 | close(log_file_fd); 96 | if (log_file_name) 97 | free(log_file_name); 98 | } 99 | -------------------------------------------------------------------------------- /c_src/debug.c: -------------------------------------------------------------------------------- 1 | /* 2 | erld - A UNIX-style daemon wrapper for Erlang 3 | Copyright (C) 2012 ShoreTel Inc. 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | /* Debugging and error checking macros and functions */ 21 | /* Yet another set of debugging functions. Unfortunately, passing arguments through 22 | * "..." prevents gcc from applying it's magic printf argument checking but the 23 | * only other option is macro programming which is even worse... */ 24 | 25 | #include 26 | #include /* getpid */ 27 | #include 28 | #include /* errno */ 29 | #include /* strerror */ 30 | #include /* localtime */ 31 | #include /* gettimeofday */ 32 | 33 | #include "debug.h" 34 | #include "log.h" 35 | #include "util.h" 36 | 37 | int debug_flag = 0; 38 | 39 | void log_impl(int prefix, int suffix, const char *fun, const char *fmt, va_list ap); 40 | 41 | char *create_fmt_errno_message(const char *file, int line, int e, const char *fmt, va_list ap) { 42 | char *msg1, *msg2; 43 | va_list tmp_ap; 44 | 45 | va_copy(tmp_ap, ap); 46 | msg1 = alloc_vprintf(fmt, tmp_ap); 47 | va_end(tmp_ap); 48 | if (msg1) { 49 | if (e) 50 | msg2 = alloc_printf("fatal error (file %s, line %d) in \"%s\": error %d (%s)", file, line, msg1, e, strerror(e)); 51 | else 52 | msg2 = alloc_printf("fatal error (file %s, line %d): \"%s\"", file, line, msg1); 53 | } 54 | else { 55 | if (e) 56 | msg2 = alloc_printf("fatal error (file %s, line %d): error %d (%s)", file, line, e, strerror(e)); 57 | else 58 | msg2 = alloc_printf("fatal error (file %s, line %d)", file, line); 59 | } 60 | free(msg1); 61 | return msg2; 62 | } 63 | 64 | void fatal_error(const char *file, int line, int e, const char *fmt, ...) { 65 | va_list ap; 66 | char *msg; 67 | 68 | va_start(ap, fmt); 69 | msg = create_fmt_errno_message(file, line, e, fmt, ap); 70 | va_end(ap); 71 | 72 | LOG_V("%s", msg); 73 | syslog(LOG_ERR, "%s", msg); 74 | free(msg); 75 | exit(1); 76 | } 77 | 78 | void debug(const char *fun, const char *fmt, ...) { 79 | va_list ap; 80 | 81 | if (!debug_flag) 82 | return; 83 | 84 | va_start(ap, fmt); 85 | log_impl(1, 1, fun, fmt, ap); 86 | va_end(ap); 87 | 88 | } 89 | 90 | void log_message(int prefix, int suffix, const char *fun, const char *fmt, ...) { 91 | va_list ap; 92 | 93 | va_start(ap, fmt); 94 | log_impl(prefix, suffix, fun, fmt, ap); 95 | va_end(ap); 96 | } 97 | 98 | void log_impl(int prefix, int suffix, const char *fun, const char *fmt, va_list ap) { 99 | char *msg1, *msg2; 100 | struct timeval now; 101 | struct tm *tm; 102 | 103 | gettimeofday(&now, 0); 104 | tm = localtime(&now.tv_sec); 105 | 106 | msg1 = alloc_vprintf(fmt, ap); 107 | if (prefix) 108 | msg2 = alloc_printf("%04d-%02d-%02d %02d:%02d:%02d.%06ld %d %s: %s%s" 109 | , tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday 110 | , tm->tm_hour, tm->tm_min, tm->tm_sec, (long) now.tv_usec 111 | , getpid(), fun, msg1, (suffix ? "\n" : "")); 112 | else 113 | msg2 = alloc_printf("%s%s", msg1, (suffix ? "\n" : "")); 114 | 115 | /* Uncommenting the printf below will cause output to be displayed 116 | on the console until erld detaches. */ 117 | /* printf("%s", msg2); */ 118 | log_write(msg2, strlen(msg2)); 119 | free(msg2); 120 | free(msg1); 121 | } 122 | -------------------------------------------------------------------------------- /c_src/util.c: -------------------------------------------------------------------------------- 1 | /* 2 | erld - A UNIX-style daemon wrapper for Erlang 3 | Copyright (C) 2012 ShoreTel Inc. 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | #define _XOPEN_SOURCE 500 /* strdup, vsnprintf etc. */ 21 | #define _DARWIN_C_SOURCE /* getting WCORESUMP macro on mac */ 22 | #include 23 | #include /* kill, signal */ 24 | #include 25 | #include /* waitpid */ 26 | #include /* syslog */ 27 | #include /* printf */ 28 | #include /* malloc, free */ 29 | #include "util.h" 30 | #include "debug.h" 31 | 32 | char *alloc_vprintf(const char *fmt, va_list ap) { 33 | int l; 34 | char *msg; 35 | va_list tmp_ap; 36 | 37 | if (!fmt) 38 | return 0; 39 | va_copy(tmp_ap, ap); 40 | l = vsnprintf(0, 0, fmt, tmp_ap); 41 | va_end(tmp_ap); 42 | if (l < 0) 43 | return strdup("alloc_vprintf error"); 44 | msg = (char*)malloc(l + 1); 45 | va_copy(tmp_ap, ap); 46 | vsnprintf(msg, l + 1, fmt, tmp_ap); 47 | va_end(tmp_ap); 48 | return msg; 49 | } 50 | 51 | char *alloc_printf(const char *fmt, ...) { 52 | va_list ap; 53 | char *msg; 54 | 55 | va_start(ap, fmt); 56 | msg = alloc_vprintf(fmt, ap); 57 | va_end(ap); 58 | return msg; 59 | } 60 | 61 | void set_cleanup_handler(void (*handler)(int)) { 62 | set_signal(SIGQUIT, handler, 0); 63 | set_signal(SIGINT, handler, 0); 64 | set_signal(SIGTSTP, handler, 0); 65 | set_signal(SIGTERM, handler, 0); 66 | } 67 | 68 | /* Use sigaction() here because we need a received signal 69 | * to interrupt waitpid() or select(), and that isn't always 70 | * the case with signal(). */ 71 | void set_signal(int signum, void (*handler)(int), int flags) { 72 | struct sigaction sa; 73 | 74 | memset(&sa, 0, sizeof sa); 75 | sa.sa_handler = handler; 76 | sa.sa_flags = flags; 77 | CHECK(sigaction(signum, &sa, 0)); 78 | } 79 | 80 | int cleanup_child(int sig, const char *child_name, pid_t child_pid) { 81 | int rv, status, exit_code = 0; 82 | char *exit_status; 83 | 84 | if (child_pid == -1) 85 | return 0; 86 | /* If we're here because of a signal (arg != 0), pass it on to erld */ 87 | if (sig) { 88 | LOG_V("passing signal %d to %s", sig, child_name); 89 | kill(child_pid, sig); 90 | } 91 | DEBUG_V("Waiting for %s (pid %d) to exit.", child_name, child_pid); 92 | while (((rv = waitpid(child_pid, &status, 0)) == -1) && (errno == EINTR)) 93 | /* empty */; 94 | CHECK(rv); 95 | get_exit_status(status, &exit_status, &exit_code); 96 | DEBUG("Wait complete."); 97 | LOG_V("%s exited with status %d: %s.", child_name, exit_code, exit_status); 98 | syslog(LOG_INFO, "%s exited with status %d: %s.", child_name, exit_code, exit_status); 99 | free(exit_status); 100 | return exit_code; 101 | } 102 | 103 | void get_exit_status(int status, char **exit_status, int *exit_code) { 104 | if (WIFEXITED(status)) { 105 | *exit_status = alloc_printf("exited with code %d", WEXITSTATUS(status)); 106 | if (exit_code) 107 | *exit_code = WEXITSTATUS(status); 108 | } 109 | else if (WIFSIGNALED(status)) { 110 | if(WCOREDUMP(status)) 111 | *exit_status = alloc_printf("terminated by signal %d (core dumped)", WTERMSIG(status)); 112 | else 113 | *exit_status = alloc_printf("terminated by signal %d (no core dump)", WTERMSIG(status)); 114 | } 115 | else 116 | *exit_status = alloc_printf("exited for unknown reason (no exit code or singal)"); 117 | } 118 | -------------------------------------------------------------------------------- /c_src/slay.c: -------------------------------------------------------------------------------- 1 | /* 2 | erld - A UNIX-style daemon wrapper for Erlang 3 | Copyright (C) 2012 ShoreTel Inc. 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | #include /* malloc */ 21 | #include /* */ 22 | #include /* kill */ 23 | #include /* sleep */ 24 | #include "debug.h" 25 | #include "options.h" 26 | 27 | #ifdef __linux__ 28 | #include /* readproctab */ 29 | 30 | void slay_tree(int sig, pid_t pid, int include_children); 31 | void slay_impl(proc_t **procs, int sig, pid_t pid, int include_children); 32 | 33 | /* 34 | To have the best possible chance of cleaning up all processes, take a three phase 35 | approach: 36 | - Send a SIGQUIT ("terminal quit") to the pid and wait out the grace period. 37 | - If pid, or any of pid's children are still running: 38 | - Send a SIGSTOP to freeze them. 39 | - Send a SIGKILL to kill them. 40 | */ 41 | void slay(pid_t pid) { 42 | /* First ask nicely: Just send a SIGQUIT to the top process, leaving it up 43 | to that process to shut down it's children. */ 44 | slay_tree(SIGQUIT, pid, 0); /* Use slay_tree w/ no children so that we can see pid's run state */ 45 | sleep(grace_period); /* Give time to clean up and exit */ 46 | slay_tree(SIGSTOP, pid, 1); /* First send SIGSTOP to freeze them, so they can't trigger SIGCHILD handlers in each other. */ 47 | slay_tree(SIGKILL, pid, 1); /* Then do the actual killing with SIGKILL. */ 48 | } 49 | 50 | /* Re-read the proc list each time we traverse it, because 51 | sending signals may cause it to change. */ 52 | void slay_tree(int sig, pid_t pid, int include_children) { 53 | proc_t **procs; 54 | int i; 55 | 56 | procs = readproctab(PROC_FILLSTAT); 57 | slay_impl(procs, sig, pid, include_children); 58 | for (i = 0; procs[i]; i++) { 59 | free(procs[i]); 60 | } 61 | free(procs); 62 | } 63 | 64 | void slay_impl(proc_t **procs, int sig, pid_t pid, int include_children) { 65 | int i, hit = 0; 66 | 67 | for (i = 0; procs[i]; i++) { 68 | if (procs[i]->tgid == pid) { 69 | if (procs[i]->state == 'Z') { 70 | LOG_V("Not killing PID %d because it's already died (in state 'Z').", pid); 71 | } 72 | else { 73 | LOG_V("Kill PID %d (in state '%c') with signal %d", pid, procs[i]->state, sig); 74 | kill(pid, sig); 75 | } 76 | hit = 1; 77 | } 78 | if (include_children && (procs[i]->ppid == pid)) { 79 | if (procs[i]->state == 'Z') { 80 | LOG_V("Not killing PID %d because it's already died (in state 'Z').", procs[i]->tgid); 81 | } 82 | else 83 | slay_impl(procs, sig, procs[i]->tid, include_children); 84 | } 85 | } 86 | if (!hit) { 87 | LOG_V("PID %d was missing from the proc list (while sending signal %d)", pid, sig); 88 | } 89 | } 90 | #endif 91 | 92 | /* Not well tested, see above for comments. */ 93 | #ifdef __APPLE__ 94 | #include /* proc_listpids, proc_pidinfo */ 95 | #include 96 | 97 | void slay_tree(int sig, pid_t pid, int include_children); 98 | void slay_impl(pid_t *pids, int n, int sig, pid_t pid, int include_children); 99 | 100 | void slay(pid_t pid) { 101 | slay_tree(SIGQUIT, pid, 0); 102 | sleep(grace_period); 103 | slay_tree(SIGSTOP, pid, 1); 104 | slay_tree(SIGKILL, pid, 1); 105 | } 106 | 107 | void slay_tree(int sig, pid_t pid, int include_children) { 108 | int l, n; 109 | pid_t *pids; 110 | 111 | CHECK(l = proc_listpids(PROC_ALL_PIDS, 0, 0, 0)); /* Determine max length */ 112 | pids = (pid_t *const)malloc(l); 113 | CHECK(n = proc_listpids(PROC_ALL_PIDS, 0, pids, l)); 114 | slay_impl(pids, n, sig, pid, include_children); 115 | free(pids); 116 | } 117 | 118 | void slay_impl(pid_t *pids, int n, int sig, pid_t pid, int include_children) { 119 | int i, hit = 0; 120 | 121 | DEBUG_V("slay %d with signal %d", pid, sig); 122 | kill(pid, sig); 123 | for (i = 0; i < n; i++) { 124 | struct proc_bsdinfo info; 125 | CHECK(proc_pidinfo(pids[i], PROC_PIDTBSDINFO, 0, &info, sizeof info)); 126 | if (info.pbi_pid == pid) { 127 | if (info.pbi_status == SZOMB) { 128 | LOG_V("Not killing PID %d because it's already died (in state 'Z').", pid); 129 | } 130 | else { 131 | LOG_V("Kill PID %d (in state 0x%x) with signal %d", pid, info.pbi_status, sig); 132 | kill(pid, sig); 133 | } 134 | hit = 1; 135 | } 136 | if (include_children && (info.pbi_ppid == pid)) { 137 | if (info.pbi_status == SZOMB) { 138 | LOG_V("Not killing PID %d because it's already died (in state 'Z').", info.pbi_pid); 139 | } 140 | else 141 | slay_impl(pids, n, sig, info.pbi_pid, include_children); 142 | } 143 | } 144 | if (!hit) { 145 | LOG_V("PID %d was missing from the proc list (while sending signal %d)", pid, sig); 146 | } 147 | } 148 | #endif 149 | -------------------------------------------------------------------------------- /example/erldapp.init: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # The MIT License (MIT) 4 | # Copyright (C) 2012 ShoreTel Inc. 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | # THE SOFTWARE. 23 | # 24 | # A sample init script for managing an erld application 25 | # 26 | 27 | # Uncomment and edit this if you're running from an environment that doesn't have HOME set (such as during boot): 28 | # HOME=/home/somedir 29 | 30 | NAME=erldapp 31 | DESC="My erld app" 32 | ERLD="/usr/local/bin/erld" 33 | ERLD_LOG="/var/log/$NAME/erld.log" 34 | ERL="/usr/lib/erlang/bin/erl +Ktrue +B -noinput" 35 | ERL_CONFIG="/etc/$NAME/erld-sys" 36 | ERL_BOOT="/usr/lib/erlang/lib/$NAME-1.1.1/ebin/$NAME" 37 | COOKIE_FILE="/etc/$NAME/cookie" 38 | NODE="$NAME" 39 | TMPNODE="$NODE-tmp" 40 | FULLNODE="'$NODE@`hostname -f`'" 41 | TIMEOUT=60 # Wait for one minute for the app to come up 42 | HEARTBEAT=22 # erld heartbeat timeout, seconds 43 | HEARTBEAT_WARN=8 # erld slow heartbeat warning, seconds 44 | GRACE=10 # erld kill grace period, seconds 45 | RESTART_COUNT=3 # erld restart limit (number of successive "quick" crashes before giving up) 46 | RESTART_INTERVAL=1200 # erld restart interval (time that's considered a "quick" crash) (1200 seconds = 20 minutes) 47 | PIDFILE=/var/run/$NAME.pid 48 | LOCKDIR=/var/lock 49 | LOCKFILE=$LOCKDIR/$NAME 50 | COOKIE_MOD=$NAME 51 | 52 | # This module:function will be called when erld receives a SIGHUP 53 | ROTATION_MODULE=erld_logger 54 | ROTATION_FUNCTION=rotate_logs 55 | 56 | RETVAL=0 57 | # Set to -d to enable debugging of erld. 58 | ERLD_DEBUG="" 59 | 60 | ERL_COMMAND="$ERL -config $ERL_CONFIG -name $NODE -boot $ERL_BOOT -shutdown_time 5000 +W w" 61 | ERLD_COMMAND="$ERLD $ERLD_DEBUG -c $COOKIE_FILE -l $ERLD_LOG -p $PIDFILE -t $HEARTBEAT -T $HEARTBEAT_WARN -g $GRACE -r $RESTART_COUNT -i $RESTART_INTERVAL -M $ROTATION_MODULE -F $ROTATION_FUNCTION -- $ERL_COMMAND" 62 | 63 | checkroot() { 64 | if [ `id -u` -ne "0" ]; then 65 | echo "Only the super user can successfully run this command." 66 | exit 1 67 | fi 68 | } 69 | 70 | success() { 71 | echo "OK" 72 | RETVAL=0 73 | } 74 | 75 | failure() { 76 | echo "FAILED (check erld.log for errors)" 77 | RETVAL=1 78 | } 79 | 80 | waitstop() { 81 | pid=$1 82 | echo -n "(wait for $pid:" 83 | 84 | count=0 85 | ps -p $pid > /dev/null 86 | dead=$? 87 | while [[ "$dead" -eq "0" && "$count" -lt "10" ]]; do 88 | echo -n '.' 89 | sleep 1 90 | (( count++ )) 91 | ps -p $pid > /dev/null 92 | dead=$? 93 | done 94 | echo -n ') ' 95 | # If it didn't shut down nicely, we get nasty 96 | if [ "$dead" -eq "0" ]; then 97 | echo -e "\nWARNING: Failed to gracefully shut down $NAME node. Resorting to killing it harshly." 98 | kill -9 $pid 99 | fi 100 | } 101 | 102 | test -x $START || exit 0 103 | 104 | start() { 105 | checkroot 106 | # Make sure epmd is running. There's no harm in running it several times and it doesn't 107 | # need to be stopped so just run it simply here every time. 108 | epmd -daemon 109 | bake_cookie 110 | echo -n "Starting the $DESC server... " 111 | $ERLD_COMMAND 112 | if [ "$?" -eq "0" ]; then 113 | success 114 | [ -d $LOCKDIR ] || mkdir -p $LOCKDIR 115 | touch $LOCKFILE 116 | else 117 | failure 118 | fi 119 | } 120 | 121 | bake_cookie() { 122 | touch $COOKIE_FILE 123 | chown root $COOKIE_FILE 124 | chmod o= $COOKIE_FILE 125 | chmod g=r $COOKIE_FILE 126 | $ERL -boot start_clean -noshell -eval "io:fwrite(\"~s\", [$COOKIE_MOD:bake_cookie()])." -s init stop > $COOKIE_FILE 127 | } 128 | 129 | stop() { 130 | checkroot 131 | if [ -r $PIDFILE ]; then 132 | echo -n "Stopping the $DESC server " 133 | pid=`cat $PIDFILE` 134 | $ERL -name $TMPNODE -eval "erld_remote:stop(\"$NAME\", $COOKIE_MOD)." 135 | if [ "$?" -eq "0" ] ; then 136 | waitstop $pid 137 | rm -f $LOCKFILE 138 | rm -f $PIDFILE 139 | success 140 | else 141 | failure 142 | fi 143 | 144 | else 145 | echo "$DESC already stopped" 146 | fi 147 | } 148 | 149 | condrestart() { 150 | checkroot 151 | [ -e $LOCKFILE ] && stop && start 152 | } 153 | 154 | condstart() { 155 | checkroot 156 | [ -e $LOCKFILE ] || start 157 | } 158 | 159 | condstop() { 160 | checkroot 161 | [ -e $LOCKFILE ] && stop 162 | } 163 | 164 | # Status result codes as defined by LSB 165 | status() { 166 | echo -n "The $DESC server is " 167 | if [ -e $LOCKFILE ]; then 168 | if [ -e $PIDFILE ]; then 169 | pid=`cat $PIDFILE` 170 | if ps -p $pid > /dev/null; then 171 | # running and pid file exists 172 | echo "running" 173 | RETVAL=0 174 | else 175 | # not running but pid file exists 176 | echo "dead but $LOCKFILE and $PIDFILE exist" 177 | RETVAL=1 178 | fi 179 | else 180 | # not running, no pid file 181 | echo "dead but $LOCKFILE file eixsts" 182 | RETVAL=2 183 | fi 184 | else 185 | # no lock file 186 | echo "not running" 187 | RETVAL=3 188 | fi 189 | } 190 | 191 | show() 192 | { 193 | echo "The server is started in the following environmnent:" 194 | echo "(HOME is already set when running interactively.)" 195 | echo "export HOME='$HOME'" 196 | echo "The command used to start the server is:" 197 | echo "$ERLD_COMMAND" | sed 's/\"/\\&/g' 198 | echo "The command run by erld is:" 199 | echo "$ERL_COMMAND" | sed 's/\"/\\&/g' 200 | echo "To run without overriding the error logger remove the \"-config\" option." 201 | } 202 | 203 | case "$1" in 204 | start) 205 | start 206 | ;; 207 | stop) 208 | stop 209 | ;; 210 | restart) 211 | stop 212 | start 213 | ;; 214 | condstop) 215 | condstop 216 | ;; 217 | condstart) 218 | condstart 219 | ;; 220 | condrestart) 221 | condrestart 222 | ;; 223 | status) 224 | status 225 | ;; 226 | show) 227 | show 228 | ;; 229 | *) 230 | echo "Usage: $0 {start|stop|restart|condrestart|condstop|show|status}" 231 | exit 1 232 | esac 233 | 234 | exit $RETVAL 235 | -------------------------------------------------------------------------------- /c_src/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | erld - A UNIX-style daemon wrapper for Erlang 3 | Copyright (C) 2012 ShoreTel Inc. 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | // #define _XOPEN_SOURCE 500 /* Causes waitpid to be interrupted by signals (SIGUSR1). */ TODO 21 | 22 | // Mac has no O_LARGEFILE parameter - I believe by default all files are 'large files'. 23 | #ifdef __APPLE__ 24 | #define O_LARGEFILE 0 25 | #endif 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include "version.h" 42 | #include "debug.h" 43 | #include "global.h" 44 | #include "log.h" 45 | #include "pid.h" 46 | #include "util.h" 47 | #include "erld.h" 48 | #include "cnode.h" 49 | 50 | /* values that must be global because they are accessed in cleanup (signal) handlers */ 51 | static pid_t erld_pid = -1; 52 | static int daemonised = 0; 53 | 54 | /* options */ 55 | int foreground = 0; 56 | struct timeval heartbeat_timeout; 57 | struct timeval heartbeat_warn; 58 | struct timeval grace_period; 59 | int restart_limit = 0; 60 | struct timeval restart_interval; 61 | 62 | void usage(); 63 | void version(); 64 | int terminal_main(int erld_pid); 65 | void cleanup_terminal(int arg); 66 | void daemonise(int arg); 67 | 68 | int main(int argc, char *const argv[]) { 69 | char c, *cookie = 0; 70 | 71 | timerclear(&heartbeat_timeout); 72 | timerclear(&heartbeat_warn); 73 | timerclear(&grace_period); 74 | grace_period.tv_sec = DEFAULT_GRACE_PERIOD; 75 | timerclear(&restart_interval); 76 | while ((c = getopt(argc, argv, "fl:p:c:dn:t:T:r:i:g:M:F:hv")) != -1) { 77 | switch (c) { 78 | case 'f': foreground = 1; break; 79 | case 'l': log_file_name = strdup(optarg); break; 80 | case 'p': pid_file_name = strdup(optarg); break; 81 | case 'c': cookie = strdup(optarg); break; 82 | case 'd': debug_flag = 1; break; 83 | case 'n': node_name = strdup(optarg); break; 84 | case 't': heartbeat_timeout.tv_sec = atol(optarg); break; 85 | case 'T': heartbeat_warn.tv_sec = atol(optarg); break; 86 | case 'r': restart_limit = atoi(optarg); break; 87 | case 'i': restart_interval.tv_sec = atol(optarg); break; 88 | case 'g': grace_period.tv_sec = atol(optarg); break; 89 | case 'M': log_rotation_module = strdup(optarg); break; 90 | case 'F': log_rotation_function = strdup(optarg); break; 91 | case 'h': usage(); exit(0); break; 92 | case 'v': version(); exit(0); break; 93 | default: printf("Unknown option: %c\n", c); 94 | } 95 | } 96 | 97 | if (optind >= argc) { 98 | usage(); 99 | exit(0); 100 | } 101 | 102 | if (debug_flag) 103 | ei_tracelevel = 99; 104 | 105 | set_signal(SIGUSR1, daemonise, 0); 106 | 107 | if (foreground) { 108 | DEBUG("going directly to erld_main"); 109 | return erld_main(cookie, argv + optind); 110 | } 111 | else { 112 | DEBUG_V("forking to start %s", NAME); 113 | if (!(erld_pid = fork())) { 114 | set_signal(SIGUSR1, SIG_DFL, 0); // Clear the SIGUSR1 handler we've inherited. 115 | return erld_main(cookie, argv + optind); 116 | } 117 | set_cleanup_handler(cleanup_terminal); 118 | return terminal_main(erld_pid); 119 | } 120 | } 121 | 122 | /* This is the main loop of the original process (main). 123 | * It just sits at the terminal, waiting to be told to 124 | * exit with a particular exit code (either by an explicit 125 | * signal or the death of it's child. */ 126 | int terminal_main(int erld_pid) { 127 | int rv, status; 128 | char *status_message; 129 | 130 | DEBUG("waiting for daemonize"); 131 | while (1) { 132 | DEBUG("waitpid"); 133 | rv = waitpid(erld_pid, &status, 0); 134 | DEBUG("wait complete"); 135 | if (daemonised) { 136 | /* If we've got the OK, we don't care what happened to end the wait */ 137 | DEBUG(NAME " startup successful, exiting."); 138 | exit(0); 139 | } 140 | if (rv == -1) { 141 | if (errno != EINTR) 142 | FATAL_ERROR(1, "waitpid"); 143 | } 144 | else { 145 | if (rv == erld_pid) { 146 | DEBUG(NAME " has exited"); 147 | if (!WIFEXITED(status) || (WEXITSTATUS(status) != 1)) { 148 | /* A status of 1 indicates that the daemon has already written an error message */ 149 | /* otherwise... */ 150 | get_exit_status(status, &status_message, 0); 151 | fprintf(stderr, "Startup failed, " NAME " crashed: %s\n", status_message); 152 | free(status_message); 153 | } 154 | exit(1); 155 | } 156 | else 157 | DEBUG_V("unknown child (pid %d) has exited, ignored", rv); 158 | } 159 | } 160 | } 161 | 162 | void usage() { 163 | printf("%s [options] -- \n", NAME); 164 | printf( 165 | "-f run in the foreground\n" 166 | "-l , default: \"%s\"\n" 167 | "-p , default: not used\n" 168 | "-c , default: $HOME/%s\n" 169 | "-n , default: %s-\n" 170 | "-t , default: disabled\n" 171 | "-T , default: disabled\n" 172 | "-r , default: unlimited\n" 173 | "-i