Cannot run my go program as a service on Windows server 2019 - windows

I want to run a REST API written in Go as a Windows service, but when I try to start the service I get an error.
This is how I install the service with Powershell:
$params = #{
Name = "AHS"
BinaryPathName = '"C:\Temp\ah.exe --service"'
DisplayName = "AH Service"
StartupType = "Manual"
Description = "This is a test service."
}
New-Service #params
This is how I start the service:
Start-Service "AHS"
This is the error I get:
At C:\Windows\Temp\script-62cfbba7-a4ef-b024-51ce-ba0b6804cbd7.ps1:10 char:1
+ Start-Service "AHS"
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (System.ServiceProcess.ServiceController:ServiceController) [Start-Service],
ServiceCommandException
+ FullyQualifiedErrorId : CouldNotStartService,Microsoft.PowerShell.Commands.StartServiceCommand
This is a simplified version of the code just to sort out running it as a service:
package main
import (
"fmt"
"os"
"time"
"github.com/kardianos/service"
)
const serviceName = "AHS"
type program struct{}
func (p program) Start(s service.Service) error {
fmt.Println(s.String() + " started")
go p.run()
return nil
}
func (p program) Stop(s service.Service) error {
fmt.Println(s.String() + " stopped")
return nil
}
func (p program) run() {
str := "hello, world \n"
fileName := "log.txt"
for {
err := writeToFile(str, fileName)
if err != nil {
fmt.Println(err)
}
time.Sleep(1 * time.Second)
}
}
func main() {
serviceConfig := &service.Config{
Name: serviceName,
}
prg := &program{}
s, err := service.New(prg, serviceConfig)
if err != nil {
fmt.Println("Cannot create the service: " + err.Error())
}
err = s.Run()
if err != nil {
fmt.Println("Cannot start the service: " + err.Error())
}
}
func writeToFile(str string, fileName string) error {
file, err := os.Create(fileName)
if err != nil {
if !os.IsExist(err) {
return err
}
}
defer file.Close()
_, err = file.WriteString(str)
if err != nil {
return err
}
return nil
}

Related

Can't run the go routines while building chat app with golang

I'm trying to build a chat app with golang. However, I've encountered a problem that if I wrap the code of the for loop into a Sender() function and using goroutines, the client will shut down immediately. But if I put it in the main function, it can run correctly.
Here is the client code.
package main
import (
"fmt"
"net"
"os"
"os/signal"
"syscall"
)
const (
SERVER_HOST = "localhost"
SERVER_PORT = "9988"
SERVER_TYPE = "tcp"
)
var connection net.Conn
var err error
func main() {
SetupCloseHandler()
//establish connection
connection, err = net.Dial(SERVER_TYPE, SERVER_HOST+":"+SERVER_PORT)
if err != nil {
panic(err)
}
//receive some data
go Receiver()
// go Sender()
for {
var input string
fmt.Print("Enter text: ")
fmt.Scan(&input)
_, err = connection.Write([]byte(input))
if err != nil {
panic(err)
}
}
}
// func Sender() {
// for {
// var input string
// fmt.Print("Enter text: ")
// fmt.Scan(&input)
// _, err = connection.Write([]byte(input))
// if err != nil {
// panic(err)
// }
// }
// }
func Receiver() {
for {
buffer := make([]byte, 1024)
mLen, err := connection.Read(buffer)
if err != nil {
panic(err)
}
fmt.Println("Received: ", string(buffer[:mLen]))
}
}
func SetupCloseHandler() {
c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
fmt.Println("\r- Ctrl+C pressed in Terminal")
os.Exit(0)
}()
}
Here is the server code.
package main
import (
"fmt"
"net"
"os"
"github.com/google/uuid"
)
const (
SERVER_HOST = "localhost"
SERVER_PORT = "9988"
SERVER_TYPE = "tcp"
)
var clientMap = make(map[string]net.Conn)
func main() {
fmt.Println("Server Running...")
server, err := net.Listen(SERVER_TYPE, SERVER_HOST+":"+SERVER_PORT)
if err != nil {
fmt.Println("Error listening:", err.Error())
os.Exit(1)
}
defer server.Close()
fmt.Println("Listening on " + SERVER_HOST + ":" + SERVER_PORT)
fmt.Println("Waiting for client...")
for {
connection, err := server.Accept()
id := uuid.New().String()
clientMap[id] = connection
if err != nil {
fmt.Println("Error accepting: ", err.Error())
os.Exit(1)
}
fmt.Printf("client %s connected\n", id)
go processClient(connection, id)
}
}
func processClient(connection net.Conn, id string) {
defer connection.Close()
for {
buffer := make([]byte, 1024)
mLen, err := connection.Read(buffer)
if err != nil {
fmt.Println("Error reading:", err.Error())
delete(clientMap, id)
connection.Close()
return
}
fmt.Println("Received: ", string(buffer[:mLen]))
for k, c := range clientMap {
if k != id {
c.Write(buffer[:mLen])
}
}
}
}

Why does golang concurrent uploading of files to S3 bucket result in canceled, context deadline exceeded

I have written a small golang piece of code to recursive traverse a directory and upload the files in the director. There are approximately 93K+ items in the directory.
After a while I get the following error:
Got error uploading file: /Users/randolphhill/Fat-Tree-Business/SandBox/DDD/heydoc/ios/Pods/gRPC-Core/src/core/ext/transport/chttp2/alpn/alpn.h
operation error S3: PutObject, https response error StatusCode: 0, RequestID: , HostID: , canceled, context deadline exceeded.
Below is the code snippet
func PutFile(c context.Context, api S3PutObjectAPI, input *s3.PutObjectInput) (*s3.PutObjectOutput, error) {
return api.PutObject(c, input)
}
func PutFileS3(dir, filename, bucket, reg string) error {
var cfg aws.Config
st, err := fthash.Filehash(dir + filename)
if err != nil {
panic("configuration error, " + err.Error())
return err
}
m := make(map[string]string)
m["hashcode"] = st
cfg, err = config.LoadDefaultConfig(context.TODO(), config.WithRegion(reg))
if err != nil {
panic("configuration error, " + err.Error())
}
client := s3.NewFromConfig(cfg)
tmp := "backup" + dir + filename
uri := strings.Replace(tmp, " ", "##,##", -1)
if checkFileOnS3(client, bucket, uri, st) {
fmt.Println(" FILE EXIST")
return nil
}
file, err2 := os.Open(dir + filename)
defer file.Close()
if err2 != nil {
fmt.Println("Unable to open file " + filename)
return err2
}
tmp = "backup" + dir + filename
//uri := "backup" + dir + filename
uri = strings.Replace(tmp, " ", "##,##", -1)
input := &s3.PutObjectInput{
Bucket: &bucket,
Key: aws.String(uri),
//Key: &filename,
Body: file,
Metadata: m,
}
ctx, cancelFn := context.WithTimeout(context.TODO(), 10*time.Second)
defer cancelFn()
_, err2 = PutFile(ctx, client, input)
if err2 != nil {
fmt.Println("Got error uploading file:", dir+filename)
fmt.Println(err2)
return err2
}
return nil
}
You've added a 10 second timeout here:
ctx, cancelFn := context.WithTimeout(context.TODO(), 10*time.Second)
defer cancelFn()
_, err2 = PutFile(ctx, client, input)
if err2 != nil {
fmt.Println("Got error uploading file:", dir+filename)
fmt.Println(err2)
return err2
}
After 10 seconds, the call to PutFile will exit with a context error. You likely just need to increase the timeout if you have files that take longer to upload.
package main
import (
// "html/template"
"log"
"net/http"
"os"
"github.com/gin-gonic/gin"
"github.com/joho/godotenv"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3/s3manager"
)
var AccessKeyID string
var SecretAccessKey string
var MyRegion string
var MyBucket string
var filepath string
//GetEnvWithKey : get env value
func GetEnvWithKey(key string) string {
return os.Getenv(key)
}
func LoadEnv() {
err := godotenv.Load(".env")
if err != nil {
log.Fatalf("Error loading .env file")
os.Exit(1)
}
}
func ConnectAws() *session.Session {
AccessKeyID = GetEnvWithKey("AWS_ACCESS_KEY_ID")
SecretAccessKey = GetEnvWithKey("AWS_SECRET_ACCESS_KEY")
MyRegion = GetEnvWithKey("AWS_REGION")
sess, err := session.NewSession(
&aws.Config{
Region: aws.String(MyRegion),
Credentials: credentials.NewStaticCredentials(
AccessKeyID,
SecretAccessKey,
"", // a token will be created when the session it's used.
),
})
if err != nil {
panic(err)
}
return sess
}
func SetupRouter(sess *session.Session) {
router := gin.Default()
router.Use(func(c *gin.Context) {
c.Set("sess", sess)
c.Next()
})
// router.Get("/upload", Form)
router.POST("/upload", UploadImage)
// router.GET("/image", controllers.DisplayImage)
_ = router.Run(":4000")
}
func UploadImage(c *gin.Context) {
sess := c.MustGet("sess").(*session.Session)
uploader := s3manager.NewUploader(sess)
MyBucket = GetEnvWithKey("BUCKET_NAME")
file, header, err := c.Request.FormFile("photo")
filename := header.Filename
//upload to the s3 bucket
up, err := uploader.Upload(&s3manager.UploadInput{
Bucket: aws.String(MyBucket),
//ACL: aws.String("public-read"),
Key: aws.String(filename),
Body: file,
})
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": "Failed to upload file",
"uploader": up,
})
return
}
filepath = "https://" + MyBucket + "." + "s3-" + MyRegion + ".amazonaws.com/" + filename
c.JSON(http.StatusOK, gin.H{
"filepath": filepath,
})
}
func main() {
LoadEnv()
sess := ConnectAws()
router := gin.Default()
router.Use(func(c *gin.Context) {
c.Set("sess", sess)
c.Next()
})
router.POST("/upload", UploadImage)
//router.LoadHTMLGlob("templates/*")
//router.GET("/image", func(c *gin.Context) {
//c.HTML(http.StatusOK, "index.tmpl", gin.H{
// "title": "Main website",
//})
//})
_ = router.Run(":4000")
}

High CPU usage when reading /dev/gpiomem

Is there something in my Golang code that would be causing high cpu usage?
The code should read a button on the GPIO pin on a Raspberry Pi 4 (4GB) and when pressed send an REST post message to another program.
I tried using more sleep and no sleep, ive tried changing the code to use a falling edge detection method on the pin. Nothing has changed the high CPU usage.
import (
"fmt"
"github.com/stianeikeland/go-rpio"
"os"
"io/ioutil"
"net/http"
"strings"
"time"
)
func main() {
fmt.Println("Starting Stream Control 0.1")
pinOpenError := rpio.Open()
if pinOpenError != nil {
fmt.Println("Pin Open Error: ", pinOpenError.Error())
os.Exit(1)
}
pin := rpio.Pin(18)
pin.Input()
pin.PullUp()
var PID string
start := time.Now()
StreamRunning := false;
fmt.Println("Reading and Controlling")
for {
res := pin.Read()
if res == 0 {
if StreamRunning == false && time.Since(start).Seconds() > 10{
var StartStreamError error
PID, StartStreamError = StartStream()
if StartStreamError != nil {
fmt.Println("Start Stream Error: ", StartStreamError.Error())
}
fmt.Println("PID = " + PID)
start = time.Now()
StreamRunning = true;
} else {
if time.Since(start).Seconds() > 10 {
StopStream(PID)
StreamRunning = false;
start = time.Now()
}
}
}
time.Sleep(100)
}
}
func postHTTP(url string, requestString string) ([]byte, error){
var blankBody []byte
payload := strings.NewReader(requestString)
client := &http.Client{}
req, clientError := http.NewRequest("POST", url, payload)
if clientError != nil {
return blankBody, clientError
}
req.Header.Add("Content-Type", "application/json")
res, requestError := client.Do(req)
if requestError != nil {
return blankBody, requestError
}
defer res.Body.Close()
returnBody, returnError := ioutil.ReadAll(res.Body)
return returnBody, returnError
}
func StartStream()(string, error) {
fmt.Println("Start Stream")
response, StreamOnError := postHTTP("http://localhost:3000/startStream","{\"devicePath\":\"/dev/video0\",\"streamName\":\"LumiPi-003\"}")
if StreamOnError != nil {
fmt.Println("Stream On Error: ", StreamOnError.Error())
return "0", StreamOnError
}
splitstrings := strings.Split(string(response),",")
for i := range splitstrings {
if strings.Contains(splitstrings[i], "\"pid\":") {
linesplit := strings.Split(splitstrings[i],":")
return linesplit[1], nil
}
}
return "0", nil
}
func StopStream(PID string)(error) {
fmt.Println("Stop Stream")
command := "{\"pid\":" + PID + "}"
_, StreamOnError := postHTTP("http://localhost:3000/stopStream",command)
if StreamOnError != nil {
fmt.Println("Stream Off Error: ", StreamOnError.Error())
return StreamOnError
}
return nil
}
Picture below shows the affect time.sleep has on the program.
The time.Sleep() function was causing the PI to crash when rpio.Open() was still open.
Opening, reading the pins and closing allowed time.Sleep() to be used with a much larger delay which in turn dropped the cpu usage because its not constantly looping.
import (
"fmt"
"github.com/stianeikeland/go-rpio"
"os"
"io/ioutil"
"net/http"
"strings"
"time"
)
func main() {
fmt.Println("Starting Stream Control 0.1")
var PID string
start := time.Now()
StreamRunning := false;
fmt.Println("Reading and Controlling")
for {
pinOpenError := rpio.Open()
if pinOpenError != nil {
fmt.Println("Pin Open Error: ", pinOpenError.Error())
os.Exit(1)
}
pin := rpio.Pin(18)
pin.Input()
pin.PullUp()
res := pin.Read()
rpio.Close()
if res == 0 {
if StreamRunning == false && time.Since(start).Seconds() > 10{
var StartStreamError error
PID, StartStreamError = StartStream()
if StartStreamError != nil {
fmt.Println("Start Stream Error: ", StartStreamError.Error())
}
fmt.Println("PID = " + PID)
start = time.Now()
StreamRunning = true;
} else {
if time.Since(start).Seconds() > 10 {
StopStream(PID)
StreamRunning = false;
start = time.Now()
}
}
}
time.Sleep(100*time.Millisecond)
}
}
func postHTTP(url string, requestString string) ([]byte, error){
var blankBody []byte
payload := strings.NewReader(requestString)
client := &http.Client{}
req, clientError := http.NewRequest("POST", url, payload)
if clientError != nil {
return blankBody, clientError
}
req.Header.Add("Content-Type", "application/json")
res, requestError := client.Do(req)
if requestError != nil {
return blankBody, requestError
}
defer res.Body.Close()
returnBody, returnError := ioutil.ReadAll(res.Body)
return returnBody, returnError
}
func StartStream()(string, error) {
fmt.Println("Start Stream")
response, StreamOnError := postHTTP("http://localhost:3000/startStream","{\"devicePath\":\"/dev/video0\",\"streamName\":\"LumiPi-003\"}")
if StreamOnError != nil {
fmt.Println("Stream On Error: ", StreamOnError.Error())
return "0", StreamOnError
}
splitstrings := strings.Split(string(response),",")
for i := range splitstrings {
if strings.Contains(splitstrings[i], "\"pid\":") {
linesplit := strings.Split(splitstrings[i],":")
return linesplit[1], nil
}
}
return "0", nil
}
func StopStream(PID string)(error) {
fmt.Println("Stop Stream")
command := "{\"pid\":" + PID + "}"
_, StreamOnError := postHTTP("http://localhost:3000/stopStream",command)
if StreamOnError != nil {
fmt.Println("Stream Off Error: ", StreamOnError.Error())
return StreamOnError
}
return nil
}

Go program memory consumption keeps increasing on Windows Server 2016

This is my Go code. I am using version 1.13
Once the code starts running, the memory consumption continuously increases, never decreasing. I dont think I am creating new variables. I am only reusing global variables for all the storage.
I read online that the GC in Go should kick in every 2 minutes...?
EDIT: I am monitoring the memory consumption of the program on the Windows Task Manager. And the Memory starts at ~5MB, when the program starts to run and increases 0.3~0.5 MB every minute.
package main
import (
"context"
"database/sql"
"encoding/csv"
"fmt"
"log"
"os"
"path/filepath"
"runtime"
"runtime/debug"
"strings"
"time"
_ "github.com/denisenkom/go-mssqldb"
)
var path = os.Args[1]
var table = os.Args[4]
var backupPath = os.Args[2]
var location = os.Args[5]
var server = "localhost"
var port = 1433
var user = "###"
var password = "###"
var database = os.Args[3]
var ctx = context.Background()
var insertQuery = "INSERT INTO [" + database + "].[dbo].[" + table + "] ([meter_id],[time_stamp],[r_voltage],[y_voltage],[b_voltage],[ry_voltage],[yb_voltage],[br_voltage],[r_current],[y_current],[b_current],[kva],[kw],[kvar],[power_factor],[freq],[kwh],[run_hr],[updated_date]) VALUES"
var updateStatusQuery = "UPDATE EMS_Location_Status SET active='100',mailed='N',Updated_Date=getdate(),Error_desc='Data is coming from FTPS' where Location_Id ='" + location + "'"
var db *sql.DB
var connString = fmt.Sprintf("server=%s;user id=%s;password=%s;port=%d;database=%s;",
server, user, password, port, database)
var err error
var files []string
var tempInsertQuery string
var csvFile *os.File
var csvLines [][]string
var i int
var line []string
var point string
var processedFile []string
var fileName string
var iterFile string
var c = 10
func processFile(file string, table string) {
fmt.Println("Processing file...", file)
tempInsertQuery = insertQuery + " "
csvFile, err = os.Open(file)
if err != nil {
for err != nil {
fmt.Println("Encountered error in opening file.. trying again in 10 seconds.")
time.Sleep(10 * time.Second)
csvFile, err = os.Open(file)
}
fmt.Println(err)
}
csvLines, err = csv.NewReader(csvFile).ReadAll()
if err != nil {
fmt.Println(err)
}
for i, line = range csvLines {
if i == 0 {
continue
} else {
tempInsertQuery = tempInsertQuery + "("
for _, point = range line {
tempInsertQuery = tempInsertQuery + "'" + point + "'" + ","
}
tempInsertQuery = tempInsertQuery[0:len(tempInsertQuery)-1] + ", GETDATE()" + "), "
}
}
tempInsertQuery = tempInsertQuery[0 : len(tempInsertQuery)-2]
// Execute query
//fmt.Println(tempInsertQuery)
_, err = db.QueryContext(ctx, tempInsertQuery)
if err != nil {
fmt.Println(err)
return
}
_, err = db.QueryContext(ctx, updateStatusQuery)
if err != nil {
fmt.Println(err)
return
}
csvFile.Close()
fmt.Println("Done processing file ", file)
backupFile(file)
runtime.GC()
debug.FreeOSMemory()
}
func backupFile(file string) {
fmt.Println("Backing up...", file)
processedFile = strings.Split(file, "\\")
fileName = processedFile[len(processedFile)-1]
err = os.Rename(file, backupPath+fileName)
if err != nil {
log.Fatal(err)
}
}
func scanner(scanPath string, table string) {
err = filepath.Walk(scanPath, func(path string, info os.FileInfo, err error) error {
if strings.Contains(path, ".csv") {
files = append(files, path)
}
return nil
})
if err != nil {
panic(err)
}
for _, iterFile = range files {
time.Sleep(2 * time.Second)
processFile(iterFile, table)
}
files = files[:0]
}
func main() {
fmt.Println(connString)
// Create connection pool
db, err = sql.Open("sqlserver", connString)
if err != nil {
fmt.Println("Error creating connection pool: ", err.Error())
}
err = db.PingContext(ctx)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Printf("Connected to server!\n")
fmt.Printf("Initiating...\n")
for c > 0 {
scanner(path, table)
time.Sleep(1 * time.Second)
}
fmt.Println("Quitting..")
}
Any help would be appreciated! Thanks.
General concept in Go is whatever implements io.Closer (that has a Close() method), when you have such a value which you don't intend to use anymore, you should call its Close() method which in general frees resources which otherwise may not be freed immediately or not at all (ever).
Your db.QueryContext() call is sql.Conn.QueryContext() which returns an *sql.Rows value which has a Close() method. Do call that. Currently you're not even storing the returned sql.Rows.
Do it like this:
var rows *sql.Rows
rows, err = db.QueryContext(ctx, tempInsertQuery)
if err != nil {
fmt.Println(err)
return
}
if err2 := rows.Close(); err2 != nil {
fmt.Println("Error closing rows:", err2
}
Apply this at all uses of Conn.QueryContext().
Whenever makes sense, use defer to call such Close() methods, so it will be executed even if your code panics or have a return statement before calling that "manually".
Such example in your code is csvFile.Close(). There are returns before that, and if one of those branch is executed, the csvFile will not be closed.
Do it like this:
csvFile, err = os.Open(file)
if err != nil {
for err != nil {
fmt.Println("Encountered error in opening file.. trying again in 10 seconds.")
time.Sleep(10 * time.Second)
csvFile, err = os.Open(file)
}
fmt.Println(err)
}
defer func() {
if err2 := csvFile.Close(); err2 != nil {
fmt.Println("Error closing csvFile:", err2
}
}()

Create a service as autorun

OS: windows/7/8/8.1/10 32bit
I have one question. How to create a service that would work like autorun?
Most applications install themselves in autorun through the registry or through C:\Users\Anon\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup. But there are those that are installing through the services, or rather as a service.
I have a code:
package main
import (
"fmt"
"strings"
"time"
"syscall"
"golang.org/x/sys/windows/svc"
"golang.org/x/sys/windows/svc/mgr"
"golang.org/x/sys/windows/svc/debug"
"log"
"os"
"path/filepath"
"golang.org/x/sys/windows/svc/eventlog"
)
var elog debug.Log
type myservice struct{}
func (m *myservice) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown | svc.AcceptPauseAndContinue
changes <- svc.Status{State: svc.StartPending}
fasttick := time.Tick(500 * time.Millisecond)
slowtick := time.Tick(2 * time.Second)
tick := fasttick
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
elog.Info(1, strings.Join(args, "-"))
loop:
for {
select {
case <-tick:
beep()
elog.Info(1, "beep")
case c := <-r:
switch c.Cmd {
case svc.Interrogate:
changes <- c.CurrentStatus
// Testing deadlock from https://code.google.com/p/winsvc/issues/detail?id=4
time.Sleep(100 * time.Millisecond)
changes <- c.CurrentStatus
case svc.Stop, svc.Shutdown:
break loop
case svc.Pause:
changes <- svc.Status{State: svc.Paused, Accepts: cmdsAccepted}
tick = slowtick
case svc.Continue:
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
tick = fasttick
default:
elog.Error(1, fmt.Sprintf("unexpected control request #%d", c))
}
}
}
changes <- svc.Status{State: svc.StopPending}
return
}
func runService(name string, isDebug bool) {
var err error
if isDebug {
elog = debug.New(name)
} else {
elog, err = eventlog.Open(name)
if err != nil {
return
}
}
defer elog.Close()
elog.Info(1, fmt.Sprintf("starting %s service", name))
run := svc.Run
if isDebug {
run = debug.Run
}
err = run(name, &myservice{})
if err != nil {
elog.Error(1, fmt.Sprintf("%s service failed: %v", name, err))
return
}
elog.Info(1, fmt.Sprintf("%s service stopped", name))
}
func startService(name string) error {
m, err := mgr.Connect()
if err != nil {
return err
}
defer m.Disconnect()
s, err := m.OpenService(name)
if err != nil {
return fmt.Errorf("could not access service: %v", err)
}
defer s.Close()
err = s.Start("is", "auto-started")
if err != nil {
return fmt.Errorf("could not start service: %v", err)
}
return nil
}
func controlService(name string, c svc.Cmd, to svc.State) error {
m, err := mgr.Connect()
if err != nil {
return err
}
defer m.Disconnect()
s, err := m.OpenService(name)
if err != nil {
return fmt.Errorf("could not access service: %v", err)
}
defer s.Close()
status, err := s.Control(c)
if err != nil {
return fmt.Errorf("could not send control=%d: %v", c, err)
}
timeout := time.Now().Add(10 * time.Second)
for status.State != to {
if timeout.Before(time.Now()) {
return fmt.Errorf("timeout waiting for service to go to state=%d", to)
}
time.Sleep(300 * time.Millisecond)
status, err = s.Query()
if err != nil {
return fmt.Errorf("could not retrieve service status: %v", err)
}
}
return nil
}
func main() {
const svcName = "Best Service"
isIntSess, err := svc.IsAnInteractiveSession()
if err != nil {
log.Fatalf("failed to determine if we are running in an interactive session: %v", err)
}
if !isIntSess {
runService(svcName, false)
return
}
/*err = controlService(svcName, svc.Stop, svc.Stopped)
err = removeService(svcName)*/
err = installService(svcName, "Best Service")
runService(svcName, true)
if err != nil {
log.Fatalf("failed to %s: %v", svcName, err)
}
return
}
func exePath() (string, error) {
prog := os.Args[0]
p, err := filepath.Abs(prog)
if err != nil {
return "", err
}
fi, err := os.Stat(p)
if err == nil {
if !fi.Mode().IsDir() {
return p, nil
}
err = fmt.Errorf("%s is directory", p)
}
if filepath.Ext(p) == "" {
p += ".exe"
fi, err := os.Stat(p)
if err == nil {
if !fi.Mode().IsDir() {
return p, nil
}
err = fmt.Errorf("%s is directory", p)
}
}
return "", err
}
func installService(name, desc string) error {
exepath, err := exePath()
if err != nil {
return err
}
m, err := mgr.Connect()
if err != nil {
return err
}
defer m.Disconnect()
s, err := m.OpenService(name)
if err == nil {
s.Close()
return fmt.Errorf("service %s already exists", name)
}
s, err = m.CreateService(name, exepath, mgr.Config{DisplayName: desc, Description: "BB service"}, "is", "auto-started")
if err != nil {
return err
}
defer s.Close()
err = eventlog.InstallAsEventCreate(name, eventlog.Error|eventlog.Warning|eventlog.Info)
if err != nil {
s.Delete()
return fmt.Errorf("SetupEventLogSource() failed: %s", err)
}
return nil
}
func removeService(name string) error {
m, err := mgr.Connect()
if err != nil {
return err
}
defer m.Disconnect()
s, err := m.OpenService(name)
if err != nil {
return fmt.Errorf("service %s is not installed", name)
}
defer s.Close()
err = s.Delete()
if err != nil {
return err
}
err = eventlog.Remove(name)
if err != nil {
return fmt.Errorf("RemoveEventLogSource() failed: %s", err)
}
return nil
}
var (
beepFunc = syscall.MustLoadDLL("user32.dll").MustFindProc("MessageBeep")
)
func beep() {
beepFunc.Call(0xffffffff)
}
Application is installed and every time I exit the application the service stops. I need that even after restarting the PC the service worked and the application started. How can I do it?
maybe it's not actual but during the creation of the service you should extend and pass Config
mgr.Config{DisplayName: desc, StartType: mgr.StartAutomatic}
like here:
s, err = m.CreateService(name, exepath, mgr.Config{DisplayName: desc, StartType: mgr.StartAutomatic}, "is", "auto-started")
if err != nil {
return err
}
Here you can find all necessary constants and functions:
https://github.com/golang/sys/blob/master/windows/svc/mgr/config.go
On Windows 10 go to Task Scheduler > Task Scheduler Library > Create Basic Task > Trigger: when the computer starts > Action: Start a program.
When running the task, use the following user account: SYSTEM.
There is a package in the standard library that does this:
https://godoc.org/golang.org/x/sys/windows/svc
example: https://github.com/golang/sys/tree/master/windows/svc/example

Resources