From dad1a00d01b0e3fe53a711713be6ce3dd68ae4d2 Mon Sep 17 00:00:00 2001 From: Igor Dolzhikov Date: Wed, 13 Aug 2014 07:12:07 +0700 Subject: [PATCH 1/5] prepare multisystem linux daemon --- linux.go | 297 +------------------------------------------------------ 1 file changed, 3 insertions(+), 294 deletions(-) diff --git a/linux.go b/linux.go index 89108a8..9b5c69d 100644 --- a/linux.go +++ b/linux.go @@ -6,307 +6,16 @@ package daemon import ( - "errors" "os" - "os/exec" - "regexp" - "text/template" ) -// LinuxRecord - standard record (struct) for linux version of daemon package -type LinuxRecord struct { - name string - description string -} - -func newDaemon(name, description string) (*LinuxRecord, error) { +// Get the daemon properly +func newDaemon(name, description string) (Daemon, error) { - return &LinuxRecord{name, description}, nil -} - -// Standard service path for system V daemons -func (linux *LinuxRecord) servicePath() string { - return "/etc/init.d/" + linux.name -} - -// Check service is installed -func (linux *LinuxRecord) checkInstalled() bool { - - if _, err := os.Stat(linux.servicePath()); err == nil { - return true - } - - return false + return &SystemVRecord{name, description}, nil } // Get executable path func execPath() (string, error) { return os.Readlink("/proc/self/exe") } - -// Check service is running -func (linux *LinuxRecord) checkRunning() (string, bool) { - output, err := exec.Command("service", linux.name, "status").Output() - if err == nil { - if matched, err := regexp.MatchString(linux.name, string(output)); err == nil && matched { - reg := regexp.MustCompile("pid ([0-9]+)") - data := reg.FindStringSubmatch(string(output)) - if len(data) > 1 { - return "Service (pid " + data[1] + ") is running...", true - } - return "Service is running...", true - } - } - - return "Service is stoped", false -} - -// Install the service -func (linux *LinuxRecord) Install() (string, error) { - installAction := "Install " + linux.description + ":" - - if checkPrivileges() == false { - return installAction + failed, errors.New(rootPrivileges) - } - - srvPath := linux.servicePath() - - if linux.checkInstalled() == true { - return installAction + failed, errors.New(linux.description + " already installed") - } - - file, err := os.Create(srvPath) - if err != nil { - return installAction + failed, err - } - defer file.Close() - - execPatch, err := executablePath(linux.name) - if err != nil { - return installAction + failed, err - } - - templ, err := template.New("daemonConfig").Parse(daemonConfig) - if err != nil { - return installAction + failed, err - } - - if err := templ.Execute( - file, - &struct { - Name, Description, Path string - }{linux.name, linux.description, execPatch}, - ); err != nil { - return installAction + failed, err - } - - if err := os.Chmod(srvPath, 0755); err != nil { - return installAction + failed, err - } - - for _, i := range [...]string{"2", "3", "4", "5"} { - if err := os.Symlink(srvPath, "/etc/rc"+i+".d/S87"+linux.name); err != nil { - continue - } - } - for _, i := range [...]string{"0", "1", "6"} { - if err := os.Symlink(srvPath, "/etc/rc"+i+".d/K17"+linux.name); err != nil { - continue - } - } - - return installAction + success, nil -} - -// Remove the service -func (linux *LinuxRecord) Remove() (string, error) { - removeAction := "Removing " + linux.description + ":" - - if checkPrivileges() == false { - return removeAction + failed, errors.New(rootPrivileges) - } - - if linux.checkInstalled() == false { - return removeAction + failed, errors.New(linux.description + " is not installed") - } - - if err := os.Remove(linux.servicePath()); err != nil { - return removeAction + failed, err - } - - for _, i := range [...]string{"2", "3", "4", "5"} { - if err := os.Remove("/etc/rc" + i + ".d/S87" + linux.name); err != nil { - continue - } - } - for _, i := range [...]string{"0", "1", "6"} { - if err := os.Remove("/etc/rc" + i + ".d/K17" + linux.name); err != nil { - continue - } - } - - return removeAction + success, nil -} - -// Start the service -func (linux *LinuxRecord) Start() (string, error) { - startAction := "Starting " + linux.description + ":" - - if checkPrivileges() == false { - return startAction + failed, errors.New(rootPrivileges) - } - - if linux.checkInstalled() == false { - return startAction + failed, errors.New(linux.description + " is not installed") - } - - if _, status := linux.checkRunning(); status == true { - return startAction + failed, errors.New("service already running") - } - - if err := exec.Command("service", linux.name, "start").Run(); err != nil { - return startAction + failed, err - } - - return startAction + success, nil -} - -// Stop the service -func (linux *LinuxRecord) Stop() (string, error) { - stopAction := "Stopping " + linux.description + ":" - - if checkPrivileges() == false { - return stopAction + failed, errors.New(rootPrivileges) - } - - if linux.checkInstalled() == false { - return stopAction + failed, errors.New(linux.description + " is not installed") - } - - if _, status := linux.checkRunning(); status == false { - return stopAction + failed, errors.New("service already stopped") - } - - if err := exec.Command("service", linux.name, "stop").Run(); err != nil { - return stopAction + failed, err - } - - return stopAction + success, nil -} - -// Status - Get service status -func (linux *LinuxRecord) Status() (string, error) { - - if checkPrivileges() == false { - return "", errors.New(rootPrivileges) - } - - if linux.checkInstalled() == false { - return "Status could not defined", errors.New(linux.description + " is not installed") - } - - statusAction, _ := linux.checkRunning() - - return statusAction, nil -} - -var daemonConfig = `#! /bin/sh -# -# /etc/rc.d/init.d/{{.Name}} -# -# Starts {{.Name}} as a daemon -# -# chkconfig: 2345 87 17 -# description: Starts and stops a single {{.Name}} instance on this system - -### BEGIN INIT INFO -# Provides: {{.Name}} -# Required-Start: $network $named -# Required-Stop: $network $named -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: This service manages the {{.Description}}. -# Description: {{.Description}} -### END INIT INFO - -# -# Source function library. -# -if [ -f /etc/rc.d/init.d/functions ]; then - . /etc/rc.d/init.d/functions -fi - -exec="{{.Path}}" -servname="{{.Description}}" - -proc=$(basename $0) -pidfile="/var/run/$proc.pid" -lockfile="/var/lock/subsys/$proc" -stdoutlog="/var/log/$proc.log" -stderrlog="/var/log/$proc.err" - -[ -e /etc/sysconfig/$proc ] && . /etc/sysconfig/$proc - -start() { - [ -x $exec ] || exit 5 - - if ! [ -f $pidfile ]; then - printf "Starting $servname:\t" - echo "$(date)" >> $stdoutlog - $exec >> $stderrlog 2>> $stdoutlog & - echo $! > $pidfile - touch $lockfile - success - echo - else - failure - echo - printf "$pidfile still exists...\n" - exit 7 - fi -} - -stop() { - echo -n $"Stopping $servname: " - killproc -p $pidfile $proc - retval=$? - echo - [ $retval -eq 0 ] && rm -f $lockfile - return $retval -} - -restart() { - stop - start -} - -rh_status() { - status -p $pidfile $proc -} - -rh_status_q() { - rh_status >/dev/null 2>&1 -} - -case "$1" in - start) - rh_status_q && exit 0 - $1 - ;; - stop) - rh_status_q || exit 0 - $1 - ;; - restart) - $1 - ;; - status) - rh_status - ;; - *) - echo $"Usage: $0 {start|stop|status|restart}" - exit 2 -esac - -exit $? -` From cae99060bd8975e0e9f3a11a911396a94fec1d5a Mon Sep 17 00:00:00 2001 From: Igor Dolzhikov Date: Wed, 13 Aug 2014 07:13:03 +0700 Subject: [PATCH 2/5] add systemv linux daemon --- linux_systemv.go | 302 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 linux_systemv.go diff --git a/linux_systemv.go b/linux_systemv.go new file mode 100644 index 0000000..ebf204d --- /dev/null +++ b/linux_systemv.go @@ -0,0 +1,302 @@ +// Copyright 2014 Igor Dolzhikov. All rights reserved. +// Use of this source code is governed by +// license that can be found in the LICENSE file. + +// Package daemon linux systemV version +package daemon + +import ( + "errors" + "os" + "os/exec" + "regexp" + "text/template" +) + +// SystemVRecord - standard record (struct) for linux systemV version of daemon package +type SystemVRecord struct { + name string + description string +} + +// Standard service path for system V daemons +func (linux *SystemVRecord) servicePath() string { + return "/etc/init.d/" + linux.name +} + +// Check service is installed +func (linux *SystemVRecord) checkInstalled() bool { + + if _, err := os.Stat(linux.servicePath()); err == nil { + return true + } + + return false +} + +// Check service is running +func (linux *SystemVRecord) checkRunning() (string, bool) { + output, err := exec.Command("service", linux.name, "status").Output() + if err == nil { + if matched, err := regexp.MatchString(linux.name, string(output)); err == nil && matched { + reg := regexp.MustCompile("pid ([0-9]+)") + data := reg.FindStringSubmatch(string(output)) + if len(data) > 1 { + return "Service (pid " + data[1] + ") is running...", true + } + return "Service is running...", true + } + } + + return "Service is stoped", false +} + +// Install the service +func (linux *SystemVRecord) Install() (string, error) { + installAction := "Install " + linux.description + ":" + + if checkPrivileges() == false { + return installAction + failed, errors.New(rootPrivileges) + } + + srvPath := linux.servicePath() + + if linux.checkInstalled() == true { + return installAction + failed, errors.New(linux.description + " already installed") + } + + file, err := os.Create(srvPath) + if err != nil { + return installAction + failed, err + } + defer file.Close() + + execPatch, err := executablePath(linux.name) + if err != nil { + return installAction + failed, err + } + + templ, err := template.New("daemonConfig").Parse(daemonConfig) + if err != nil { + return installAction + failed, err + } + + if err := templ.Execute( + file, + &struct { + Name, Description, Path string + }{linux.name, linux.description, execPatch}, + ); err != nil { + return installAction + failed, err + } + + if err := os.Chmod(srvPath, 0755); err != nil { + return installAction + failed, err + } + + for _, i := range [...]string{"2", "3", "4", "5"} { + if err := os.Symlink(srvPath, "/etc/rc"+i+".d/S87"+linux.name); err != nil { + continue + } + } + for _, i := range [...]string{"0", "1", "6"} { + if err := os.Symlink(srvPath, "/etc/rc"+i+".d/K17"+linux.name); err != nil { + continue + } + } + + return installAction + success, nil +} + +// Remove the service +func (linux *SystemVRecord) Remove() (string, error) { + removeAction := "Removing " + linux.description + ":" + + if checkPrivileges() == false { + return removeAction + failed, errors.New(rootPrivileges) + } + + if linux.checkInstalled() == false { + return removeAction + failed, errors.New(linux.description + " is not installed") + } + + if err := os.Remove(linux.servicePath()); err != nil { + return removeAction + failed, err + } + + for _, i := range [...]string{"2", "3", "4", "5"} { + if err := os.Remove("/etc/rc" + i + ".d/S87" + linux.name); err != nil { + continue + } + } + for _, i := range [...]string{"0", "1", "6"} { + if err := os.Remove("/etc/rc" + i + ".d/K17" + linux.name); err != nil { + continue + } + } + + return removeAction + success, nil +} + +// Start the service +func (linux *SystemVRecord) Start() (string, error) { + startAction := "Starting " + linux.description + ":" + + if checkPrivileges() == false { + return startAction + failed, errors.New(rootPrivileges) + } + + if linux.checkInstalled() == false { + return startAction + failed, errors.New(linux.description + " is not installed") + } + + if _, status := linux.checkRunning(); status == true { + return startAction + failed, errors.New("service already running") + } + + if err := exec.Command("service", linux.name, "start").Run(); err != nil { + return startAction + failed, err + } + + return startAction + success, nil +} + +// Stop the service +func (linux *SystemVRecord) Stop() (string, error) { + stopAction := "Stopping " + linux.description + ":" + + if checkPrivileges() == false { + return stopAction + failed, errors.New(rootPrivileges) + } + + if linux.checkInstalled() == false { + return stopAction + failed, errors.New(linux.description + " is not installed") + } + + if _, status := linux.checkRunning(); status == false { + return stopAction + failed, errors.New("service already stopped") + } + + if err := exec.Command("service", linux.name, "stop").Run(); err != nil { + return stopAction + failed, err + } + + return stopAction + success, nil +} + +// Status - Get service status +func (linux *SystemVRecord) Status() (string, error) { + + if checkPrivileges() == false { + return "", errors.New(rootPrivileges) + } + + if linux.checkInstalled() == false { + return "Status could not defined", errors.New(linux.description + " is not installed") + } + + statusAction, _ := linux.checkRunning() + + return statusAction, nil +} + +var daemonConfig = `#! /bin/sh +# +# /etc/rc.d/init.d/{{.Name}} +# +# Starts {{.Name}} as a daemon +# +# chkconfig: 2345 87 17 +# description: Starts and stops a single {{.Name}} instance on this system + +### BEGIN INIT INFO +# Provides: {{.Name}} +# Required-Start: $network $named +# Required-Stop: $network $named +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: This service manages the {{.Description}}. +# Description: {{.Description}} +### END INIT INFO + +# +# Source function library. +# +if [ -f /etc/rc.d/init.d/functions ]; then + . /etc/rc.d/init.d/functions +fi + +exec="{{.Path}}" +servname="{{.Description}}" + +proc=$(basename $0) +pidfile="/var/run/$proc.pid" +lockfile="/var/lock/subsys/$proc" +stdoutlog="/var/log/$proc.log" +stderrlog="/var/log/$proc.err" + +[ -e /etc/sysconfig/$proc ] && . /etc/sysconfig/$proc + +start() { + [ -x $exec ] || exit 5 + + if ! [ -f $pidfile ]; then + printf "Starting $servname:\t" + echo "$(date)" >> $stdoutlog + $exec >> $stderrlog 2>> $stdoutlog & + echo $! > $pidfile + touch $lockfile + success + echo + else + failure + echo + printf "$pidfile still exists...\n" + exit 7 + fi +} + +stop() { + echo -n $"Stopping $servname: " + killproc -p $pidfile $proc + retval=$? + echo + [ $retval -eq 0 ] && rm -f $lockfile + return $retval +} + +restart() { + stop + start +} + +rh_status() { + status -p $pidfile $proc +} + +rh_status_q() { + rh_status >/dev/null 2>&1 +} + +case "$1" in + start) + rh_status_q && exit 0 + $1 + ;; + stop) + rh_status_q || exit 0 + $1 + ;; + restart) + $1 + ;; + status) + rh_status + ;; + *) + echo $"Usage: $0 {start|stop|status|restart}" + exit 2 +esac + +exit $? +` From 4088ef8f622da242e10887f0e64aad6bfaf6366e Mon Sep 17 00:00:00 2001 From: Igor Dolzhikov Date: Wed, 13 Aug 2014 08:59:56 +0700 Subject: [PATCH 3/5] add systemd linux daemon --- linux.go | 4 +- linux_systemd.go | 200 +++++++++++++++++++++++++++++++++++++++++++++++ linux_systemv.go | 6 +- 3 files changed, 206 insertions(+), 4 deletions(-) create mode 100644 linux_systemd.go diff --git a/linux.go b/linux.go index 9b5c69d..e6407a7 100644 --- a/linux.go +++ b/linux.go @@ -11,7 +11,9 @@ import ( // Get the daemon properly func newDaemon(name, description string) (Daemon, error) { - + if _, err := os.Stat("/run/systemd/system"); err == nil { + return &SystemDRecord{name, description}, nil + } return &SystemVRecord{name, description}, nil } diff --git a/linux_systemd.go b/linux_systemd.go new file mode 100644 index 0000000..5fde8a1 --- /dev/null +++ b/linux_systemd.go @@ -0,0 +1,200 @@ +// Copyright 2014 Igor Dolzhikov. All rights reserved. +// Use of this source code is governed by +// license that can be found in the LICENSE file. + +// Package daemon linux systemD version +package daemon + +import ( + "errors" + "os" + "os/exec" + "regexp" + "text/template" +) + +// SystemDRecord - standard record (struct) for linux systemD version of daemon package +type SystemDRecord struct { + name string + description string +} + +// Standard service path for systemD daemons +func (linux *SystemDRecord) servicePath() string { + return "/etc/systemd/system/" + linux.name + ".service" +} + +// Check service is installed +func (linux *SystemDRecord) checkInstalled() bool { + + if _, err := os.Stat(linux.servicePath()); err == nil { + return true + } + + return false +} + +// Check service is running +func (linux *SystemDRecord) checkRunning() (string, bool) { + output, err := exec.Command("systemctl", "status", linux.name+".service").Output() + if err == nil { + if matched, err := regexp.MatchString(linux.name, string(output)); err == nil && matched { + reg := regexp.MustCompile("PID: ([0-9]+)") + data := reg.FindStringSubmatch(string(output)) + if len(data) > 1 { + return "Service (pid " + data[1] + ") is running...", true + } + return "Service is running...", true + } + } + + return "Service is stoped", false +} + +// Install the service +func (linux *SystemDRecord) Install() (string, error) { + installAction := "Install " + linux.description + ":" + + if checkPrivileges() == false { + return installAction + failed, errors.New(rootPrivileges) + } + + srvPath := linux.servicePath() + + if linux.checkInstalled() == true { + return installAction + failed, errors.New(linux.description + " already installed") + } + + file, err := os.Create(srvPath) + if err != nil { + return installAction + failed, err + } + defer file.Close() + + execPatch, err := executablePath(linux.name) + if err != nil { + return installAction + failed, err + } + + templ, err := template.New("systemDConfig").Parse(systemDConfig) + if err != nil { + return installAction + failed, err + } + + if err := templ.Execute( + file, + &struct { + Name, Description, Path string + }{linux.name, linux.description, execPatch}, + ); err != nil { + return installAction + failed, err + } + + if err := exec.Command("systemctl", "daemon-reload").Run(); err != nil { + return installAction + failed, err + } + + if err := exec.Command("systemctl", "enable", linux.name+".service").Run(); err != nil { + return installAction + failed, err + } + + return installAction + success, nil +} + +// Remove the service +func (linux *SystemDRecord) Remove() (string, error) { + removeAction := "Removing " + linux.description + ":" + + if checkPrivileges() == false { + return removeAction + failed, errors.New(rootPrivileges) + } + + if linux.checkInstalled() == false { + return removeAction + failed, errors.New(linux.description + " is not installed") + } + + if err := exec.Command("systemctl", "disable", linux.name+".service").Run(); err != nil { + return removeAction + failed, err + } + + if err := os.Remove(linux.servicePath()); err != nil { + return removeAction + failed, err + } + + return removeAction + success, nil +} + +// Start the service +func (linux *SystemDRecord) Start() (string, error) { + startAction := "Starting " + linux.description + ":" + + if checkPrivileges() == false { + return startAction + failed, errors.New(rootPrivileges) + } + + if linux.checkInstalled() == false { + return startAction + failed, errors.New(linux.description + " is not installed") + } + + if _, status := linux.checkRunning(); status == true { + return startAction + failed, errors.New("service already running") + } + + if err := exec.Command("systemctl", "start", linux.name+".service").Run(); err != nil { + return startAction + failed, err + } + + return startAction + success, nil +} + +// Stop the service +func (linux *SystemDRecord) Stop() (string, error) { + stopAction := "Stopping " + linux.description + ":" + + if checkPrivileges() == false { + return stopAction + failed, errors.New(rootPrivileges) + } + + if linux.checkInstalled() == false { + return stopAction + failed, errors.New(linux.description + " is not installed") + } + + if _, status := linux.checkRunning(); status == false { + return stopAction + failed, errors.New("service already stopped") + } + + if err := exec.Command("systemctl", "stop", linux.name+".service").Run(); err != nil { + return stopAction + failed, err + } + + return stopAction + success, nil +} + +// Status - Get service status +func (linux *SystemDRecord) Status() (string, error) { + + if checkPrivileges() == false { + return "", errors.New(rootPrivileges) + } + + if linux.checkInstalled() == false { + return "Status could not defined", errors.New(linux.description + " is not installed") + } + + statusAction, _ := linux.checkRunning() + + return statusAction, nil +} + +var systemDConfig = `[Unit] +Description={{.Description}} + +[Service] +PIDFile=/var/run/{{.Name}}.pid +ExecStartPre=/bin/rm -f /var/run/{{.Name}}.pid +ExecStart={{.Path}} +Restart=on-abort + +[Install] +WantedBy=multi-user.target +` diff --git a/linux_systemv.go b/linux_systemv.go index ebf204d..3ddbe68 100644 --- a/linux_systemv.go +++ b/linux_systemv.go @@ -19,7 +19,7 @@ type SystemVRecord struct { description string } -// Standard service path for system V daemons +// Standard service path for systemV daemons func (linux *SystemVRecord) servicePath() string { return "/etc/init.d/" + linux.name } @@ -76,7 +76,7 @@ func (linux *SystemVRecord) Install() (string, error) { return installAction + failed, err } - templ, err := template.New("daemonConfig").Parse(daemonConfig) + templ, err := template.New("sysremVConfig").Parse(sysremVConfig) if err != nil { return installAction + failed, err } @@ -200,7 +200,7 @@ func (linux *SystemVRecord) Status() (string, error) { return statusAction, nil } -var daemonConfig = `#! /bin/sh +var sysremVConfig = `#! /bin/sh # # /etc/rc.d/init.d/{{.Name}} # From d7726507da7391c0160f809ad6b65164b6e07397 Mon Sep 17 00:00:00 2001 From: Igor Dolzhikov Date: Wed, 13 Aug 2014 09:26:15 +0700 Subject: [PATCH 4/5] checkRunnig systemd enhancement --- linux_systemd.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux_systemd.go b/linux_systemd.go index 5fde8a1..5c4e78a 100644 --- a/linux_systemd.go +++ b/linux_systemd.go @@ -38,8 +38,8 @@ func (linux *SystemDRecord) checkInstalled() bool { func (linux *SystemDRecord) checkRunning() (string, bool) { output, err := exec.Command("systemctl", "status", linux.name+".service").Output() if err == nil { - if matched, err := regexp.MatchString(linux.name, string(output)); err == nil && matched { - reg := regexp.MustCompile("PID: ([0-9]+)") + if matched, err := regexp.MatchString("Active: active", string(output)); err == nil && matched { + reg := regexp.MustCompile("Main PID: ([0-9]+)") data := reg.FindStringSubmatch(string(output)) if len(data) > 1 { return "Service (pid " + data[1] + ") is running...", true From 754b1c2b0017ee66b26a9984cfb2dec00e38114d Mon Sep 17 00:00:00 2001 From: Igor Dolzhikov Date: Wed, 13 Aug 2014 09:27:41 +0700 Subject: [PATCH 5/5] Bumped version number to 0.1.4 --- daemon.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon.go b/daemon.go index 5eb8a70..37cabf9 100644 --- a/daemon.go +++ b/daemon.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. /* -Package daemon 0.1.3 for use with Go (golang) services. +Package daemon 0.1.4 for use with Go (golang) services. Package daemon provides primitives for daemonization of golang services. This package is not provide implementation of user daemon,