github.com/iDigitalFlame/xmt@v0.5.4/device/daemon_nix.go (about)

     1  //go:build !windows || noservice
     2  // +build !windows noservice
     3  
     4  // Copyright (C) 2020 - 2023 iDigitalFlame
     5  //
     6  // This program is free software: you can redistribute it and/or modify
     7  // it under the terms of the GNU General Public License as published by
     8  // the Free Software Foundation, either version 3 of the License, or
     9  // any later version.
    10  //
    11  // This program is distributed in the hope that it will be useful,
    12  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  // GNU General Public License for more details.
    15  //
    16  // You should have received a copy of the GNU General Public License
    17  // along with this program.  If not, see <https://www.gnu.org/licenses/>.
    18  //
    19  
    20  package device
    21  
    22  import (
    23  	"context"
    24  	"os"
    25  	"os/signal"
    26  	"syscall"
    27  	"time"
    28  )
    29  
    30  // Daemon starts a "Service" (on Windows devices) and will run the function
    31  // until interrupted. This function will block while running the function and
    32  // can be interrupted via the Windows service control manager or SIGNALS (on
    33  // Linux).
    34  //
    35  // Any errors during runtime or returned from the functions will be returned.
    36  //
    37  // NOTE: The 'name' argument is the service name on Windows, but is ignored
    38  // on *nix systems.
    39  func Daemon(_ string, f DaemonFunc) error {
    40  	var (
    41  		w    = make(chan os.Signal, 1)
    42  		e    = make(chan error)
    43  		x, y = context.WithCancel(context.Background())
    44  		err  error
    45  	)
    46  	signal.Notify(w, syscall.SIGINT, syscall.SIGTERM)
    47  	go func() {
    48  		e <- f(x)
    49  		close(e)
    50  	}()
    51  	select {
    52  	case err = <-e:
    53  	case <-x.Done():
    54  	}
    55  	y()
    56  	signal.Reset(syscall.SIGINT, syscall.SIGTERM)
    57  	if close(w); err != nil && err != ErrQuit {
    58  		return err
    59  	}
    60  	return nil
    61  }
    62  
    63  // DaemonTicker starts a "Service" (on Windows devices) and will run the function
    64  // every 't' duration until interrupted. This function will block while running
    65  // and can be interrupted via the Windows service control manager or SIGNALS (on
    66  // Linux).
    67  //
    68  // Returning the error 'ErrQuit' will break the loop with a non-error.
    69  //
    70  // Any errors during runtime or returned from the functions will be returned.
    71  // Non-nil (non- ErrQuit) error returns will break the loop with an error.
    72  //
    73  // NOTE: The 'name' argument is the service name on Windows, but is ignored
    74  // on *nix systems.
    75  func DaemonTicker(_ string, t time.Duration, f DaemonFunc) error {
    76  	var (
    77  		w    = make(chan os.Signal, 1)
    78  		v    = time.NewTimer(t)
    79  		x, y = context.WithCancel(context.Background())
    80  		err  error
    81  	)
    82  	signal.Notify(w, syscall.SIGINT, syscall.SIGTERM)
    83  loop:
    84  	for {
    85  		select {
    86  		case <-v.C:
    87  			if err = f(x); err != nil {
    88  				break loop
    89  			}
    90  			v.Reset(t)
    91  		case <-x.Done():
    92  			break loop
    93  		}
    94  	}
    95  	y()
    96  	v.Stop()
    97  	signal.Reset(syscall.SIGINT, syscall.SIGTERM)
    98  	if close(w); err != nil && err != ErrQuit {
    99  		return err
   100  	}
   101  	return nil
   102  }