I have got panic: runtime error: invalid memory address or nil pointer dereference in Golang - go

panic: runtime error: invalid memory address or nil pointer dereference
when i add
statusCOD, err := c.route.FindStatusCODByOrigin(ctx, req.Origin)
if err != nil {
if err != sql.ErrNoRows {
ddlogger.Log(ctx, span, "Find status cod destination and cod origin", err)
errChan <- err
return
}
}
if statusCOD != nil {
IsCODOrigin = statusCOD.IsCODOrigin
IsCODDestination = statusCOD.IsCODDestination
}
in this func
for i, v := range detailShipments {
var dtPackage repo.PackageBaseModel
go func(idx int, vShipment repo.ShipmentDetailBaseModel, dataShipmentNew repo.ShipmentCODCreateModel) {
defer wg1.Done()
randomID := commonString.RandomWithCustomCharList(c.config.ShipmentCODIDRandom, c.config.ShipmentIDCharlist)
shipmentID := fmt.Sprintf("%s%s", prefix, randomID)
dataShipmentNew.ShipmentBaseModel.ShipmentID = strings.ToUpper(shipmentID)
dataShipmentNew.ShipmentDetailBaseModel = vShipment
var commodityName string
sCategory, err := c.shipmentCategoryRepo.FindOneShipmentCategoryByID(ctx, vShipment.ShipmentCategoryID.Int64)
if err != err && err != sql.ErrNoRows {
ddlogger.Log(ctx, span, "shipmentService-CreateShipmentCOD "+shipmentID, " Failed shipmentCategoryRepo.FindOneShipmentCategoryByID", err)
} else {
if sCategory != nil {
commodityName = sCategory.CommodityName.String
}
}
req := &repo.TariffRequestModelV2{
Origin: form.DetailSender.Origin,
Destination: vShipment.Destination,
Weight: vShipment.GrossWeight / 1000,
Commodity: commodityName,
GoodsValue: int64(vShipment.GoodValues.Float64),
IsInsurance: vShipment.IsInsurance.Bool,
Product: vShipment.ProductType,
Width: vShipment.DimensionWidth.Float64,
Height: vShipment.DimensionHeight.Float64,
Length: vShipment.DimensionLength.Float64,
IsCOD: true,
CODValue: vShipment.CODValue.Float64,
}
tariff, err := c.coreSystemRepo.CheckTariffV3(ctx, req)
if err != nil {
ddlogger.Log(ctx, span, "[shipmentService-CreateShipmentCOD [coreSystemRepo.CheckTariffV3]]", err)
errChan <- err
return
}
statusCOD, err := c.route.FindStatusCODByOrigin(ctx, req.Origin)
if err != nil {
if err != sql.ErrNoRows {
ddlogger.Log(ctx, span, "Find status cod destination and cod origin", err)
errChan <- err
return
}
}
if statusCOD != nil {
IsCODOrigin = statusCOD.IsCODOrigin
IsCODDestination = statusCOD.IsCODDestination
}
................
}
wg1.Wait()
close(shipmentChan)
close(errChan)
close(amountChan)

It looks like you just created the pointer, and it doesn't point to any memory spaces, you can check all the variables in your new code to find it. e.g. ctx

Related

Calling opcua.Node.Attributes returns EOF error

I'm trying to get and return all the nodes of NodeClass Variable of my OPC UA Simulation Server starting at the root node and going down all folder nodes and object nodes that have childs. I tried to use browse-example of the gopcua repo but whenever the program gets to attrs, err := n.Attributes(...) for the second time, it returns an EOF error.
I tried to recreate a minimal example:
package main
import (
"context"
"fmt"
"log"
"github.com/gopcua/opcua"
"github.com/gopcua/opcua/id"
"github.com/gopcua/opcua/ua"
)
const (
endpoint string = "opc.tcp://<ServerAddress>"
rootNodeId string = "i=85"
)
func browse(c *opcua.Client, n *opcua.Node) error {
_, err := n.Attributes(ua.AttributeIDNodeClass, ua.AttributeIDDataType)
if err != nil {
return err
}
browseChildren := func(refType uint32) error {
refs, err := n.ReferencedNodes(refType, ua.BrowseDirectionForward, ua.NodeClassAll, true)
if err != nil {
return fmt.Errorf("references: %d: %s", refType, err)
}
fmt.Printf("found %d child refs\n", len(refs))
for _, rn := range refs {
err := browse(c, rn)
if err != nil {
return fmt.Errorf("browse children: %s", err)
}
fmt.Printf("Found a Node: %s\n", rn.ID.String())
}
return nil
}
if err := browseChildren(id.HasChild); err != nil {
return err
}
if err := browseChildren(id.Organizes); err != nil {
return err
}
return nil
}
func main() {
ctx := context.Background()
c := opcua.NewClient(endpoint)
if err := c.Connect(ctx); err != nil {
log.Fatal("Could not connect")
panic(err)
}
defer c.Close()
id, _ := ua.ParseNodeID(rootNodeId)
err := browse(c, c.Node(id))
if err != nil {
log.Fatal(err)
}
}
This is how the Prosys OPC UA Simulation Server looks like:
And this the output I get from the program:
found 0 child refs
found 5 child refs
2022/01/21 14:26:06 browse children: EOF
exit status 1
tl;dr
browseChildren := func(refType uint32) error {
refs, err := n.ReferencedNodes(refType, ua.BrowseDirectionForward, ua.NodeClassAll, true)
if err != nil {
return fmt.Errorf("references: %d: %s", refType, err)
}
fmt.Printf("found %d child refs\n", len(refs))
for _, rn := range refs {
refNodeID := ua.MustParseNodeID(rn.ID.String())
refNode := c.Node(refNodeID) // parse the referenced nodes before usage
err := browse(c, refNode)
if err != nil {
return fmt.Errorf("browse children: %s", err)
}
fmt.Printf("Found a Node: %s\n", refNode.ID.String())
}
return nil
}
Reason
The referenced nodes that are returned by n.ReferencedNodes have an invalid NodeIDType (64). The only supported NodeIDTypes are 0-5. Reparsing them gives them a proper NodeIDType. (Related Gitlab issue: https://github.com/gopcua/opcua/issues/550)

Purpose of a select statement with a single case in Go?

I'm reading the source code of MicroMDM, specifically the implementation of pollCommands (https://github.com/micromdm/micromdm/blob/5c70048c14592fc16c2107f1af1bf8e0d3a52e0b/platform/queue/queue.go#L223-L284):
func (db *Store) pollCommands(pubsub pubsub.PublishSubscriber) error {
commandEvents, err := pubsub.Subscribe(context.TODO(), "command-queue", command.CommandTopic)
if err != nil {
return errors.Wrapf(err,
"subscribing push to %s topic", command.CommandTopic)
}
go func() {
for {
select {
case event := <-commandEvents:
var ev command.Event
if err := command.UnmarshalEvent(event.Message, &ev); err != nil {
level.Info(db.logger).Log("msg", "unmarshal command event in queue", "err", err)
continue
}
cmd := new(DeviceCommand)
cmd.DeviceUDID = ev.DeviceUDID
byUDID, err := db.DeviceCommand(ev.DeviceUDID)
if err == nil && byUDID != nil {
cmd = byUDID
}
newPayload, err := plist.Marshal(ev.Payload)
if err != nil {
level.Info(db.logger).Log("msg", "marshal event payload", "err", err)
continue
}
newCmd := Command{
UUID: ev.Payload.CommandUUID,
Payload: newPayload,
}
cmd.Commands = append(cmd.Commands, newCmd)
if err := db.Save(cmd); err != nil {
level.Info(db.logger).Log("msg", "save command in db", "err", err)
continue
}
level.Info(db.logger).Log(
"msg", "queued event for device",
"device_udid", ev.DeviceUDID,
"command_uuid", ev.Payload.CommandUUID,
"request_type", ev.Payload.Command.RequestType,
)
cq := new(QueueCommandQueued)
cq.DeviceUDID = ev.DeviceUDID
cq.CommandUUID = ev.Payload.CommandUUID
msgBytes, err := MarshalQueuedCommand(cq)
if err != nil {
level.Info(db.logger).Log("msg", "marshal queued command", "err", err)
continue
}
if err := pubsub.Publish(context.TODO(), CommandQueuedTopic, msgBytes); err != nil {
level.Info(db.logger).Log("msg", "publish command to queued topic", "err", err)
}
}
}
}()
return nil
}
What puzzles me is why there is a select statement in the for loop with a single case. Would this code not be equivalent to
for {
event := <-commandEvents
var ev command.Event
...
}
It seems to me that the code could be simplified by doing away with the select statement?

Multi WATCH in Golang using go-redis/redis

I am trying to do this 4 redis calls in one transaction using https://github.com/go-redis/redis :
func getHost(key) host, score, error{
numberOfHosts, err := rdb.ZCard(key).Result()
if err != nil {
return nil, 0, err
}
host, err := rdb.ZRangeByScoreWithScores(key, redis.ZRangeBy{
Min: "0",
Max: strconv.Itoa(maxScore),
Offset: numberOfHosts - 1,
Count: 1,
}).Result()
if err != nil {
return nil, 0, err
}
score := host[0].Score
member, err := json.Marshal(host[0].Member)
if err != nil {
return nil, 0, err
}
if int(score) < maxCapacity {
rdb.ZIncrBy(key, score+1, string(member))
}
host, err := rdb.GetHost(string(member))
if err != nil {
return nil, 0, err
}
return host, int(score), nil
}
I found this https://godoc.org/github.com/go-redis/redis#Client.Watch in the doc and I am trying to do something with it but it is a bit difficult at the moment.
Is anybody already done this and can provide advice ?
EDIT :
I tried to do this :
count := func(key string) error {
txf := func(tx *redis.Tx) error {
numberOfHosts, err := tx.ZCard(key).Result()
if err != nil {
return err
}
numberOfHosts--
_, err = tx.Pipelined(func(pipe redis.Pipeliner) error {
getHostKey := func(key string, numberOfHosts int) error {
txf := func(tx *redis.Tx) error {
host, err := tx.ZRangeByScoreWithScores(key, redis.ZRangeBy{
Min: "0",
Max: strconv.Itoa(maxCapacity),
Offset: numberOfHosts,
Count: 1,
}).Result()
if err != nil {
return err
}
if len(host) == 0 {
return fmt.Errorf("Get Host did not return a host for the key: %v", key)
}
score := host[0].Score
member, err := json.Marshal(host[0].Member)
if err != nil {
return err
}
score++
_, err = tx.Pipelined(func(pipe redis.Pipeliner) error {
if int(score) < maxCapacity {
incScore := func(key string, score int, member string) (*Host, int, error) {
txf := func(tx *redis.Tx) error {
tx.ZIncrBy(key, score, string(member))
host, err := rc.GetHost(string(member))
if err != nil {
return nil, 0, err
}
return host, int(score), nil
}
}
err := rc.Client.Watch(txf, key)
if err != redis.TxFailedErr {
return err
}
}
}
})
}
err := rc.Client.Watch(txf, key)
if err != redis.TxFailedErr {
return err
}
}
if err := getHostKey(key, numberOfHosts); err != nil {
return err
}
})
}
err := rc.Client.Watch(txf, key)
if err != redis.TxFailedErr {
return err
}
}
if err := count(key); err != nil {
return nil, 0, err
But the compiler is not happy with all the variable coming from the previous watch.
I will look into LUA script

How to gently defer execution of a function that might return an error?

Most cleanup functions, especially those related to the IO operations, return an error, and normally we'd prefer to defer their execution in case if we'd not forget to call them when we're done with acquired resources. For example, at some point in the code we might write something like this:
var r *SomeResource
var err error
if r, err = Open(/* parameters */); err != nil {
return nil, err
}
defer r.Close() // This might return an error
It seems that if Close function returns an error, it'll be ignored. How can we gently process the returned error from such a function?
Using defer with a func() {}() like so.
var r *SomeResource
var err error
if r, err = Open(/* parameters */); err != nil {
return nil, err
}
defer func() {
if err = r.Close(); err != nil {
fmt.Printf("ERROR: %v", err)
}
}()
Fail gracefully with an error. Report the first error. Don't overwrite earlier errors. For example,
package main
import (
"fmt"
"os"
)
func demo() (name string, err error) {
filename := `test.file`
f, err := os.Open(filename)
if err != nil {
return "", err
}
defer func() {
e := f.Close()
if e != nil {
if err == nil {
err = e
}
}
}()
// do someting with the file
name = f.Name()
fi, err := f.Stat()
if err != nil {
return name, err
}
if fi.Size() == 0 {
err = fmt.Errorf("%s: empty file", filename)
return name, err
}
return name, err
}
func main() {
name, err := demo()
fmt.Println(name, err)
}
We can handle this in ways like:
way-1:
func myFn() error {
var err error
if r, err = Open(/* parameters */); err != nil {
return nil, err
}
defer func() {
if cErr = r.Close(); cErr != nil {
err = cErr
}
}()
return err
}
way-2:
func myFn() error {
var err error
if r, err = Open(/* parameters */); err != nil {
return nil, err
}
defer func() {
if cErr = r.Close(); cErr != nil {
// we can log the error
// or
// whatever we want to do
}
}()
return err
}
I have also find a nice blog on this topic, i mean handling error when defer func returns an error. Check here https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-iii-36a1ab3d6ef1.

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