Skip to content

Commit

Permalink
Implement Modem/Device Management using HylaFax FIFOs
Browse files Browse the repository at this point in the history
  • Loading branch information
Markus Lindenberg committed May 7, 2014
1 parent c15e2cd commit 25911b9
Show file tree
Hide file tree
Showing 11 changed files with 461 additions and 17 deletions.
188 changes: 188 additions & 0 deletions src/gofaxd/device.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
package main

import (
"errors"
"gofaxlib"
"gofaxlib/logger"
"os"
"path/filepath"
"syscall"
)

const (
STATE_READY = iota
STATE_BUSY
STATE_DOWN
STATE_LOCKED

FIFO_PREFIX = "FIFO."
STATUS_DIR = "status"
)

type Device struct {
Name string
fifoname string
statusfile string
fifostream gofaxlib.FifoStream

stateSet chan uint
stateGet chan uint
errors chan error
}

func NewDevice(name string) (*Device, error) {
var err error

d := Device{
Name: name,

fifoname: filepath.Join(gofaxlib.Config.Hylafax.Spooldir, FIFO_PREFIX+name),
statusfile: filepath.Join(gofaxlib.Config.Hylafax.Spooldir, STATUS_DIR, name),

stateSet: make(chan uint),
stateGet: make(chan uint),
errors: make(chan error),
}

// Create device FIFO
stat, err := os.Stat(d.fifoname)
if err != nil {
if os.IsNotExist(err) {
if err = syscall.Mkfifo(d.fifoname, 0600); err != nil {
return nil, err
}
} else {
return nil, err
}
} else {
if stat.Mode()&os.ModeNamedPipe == 0 {
return nil, errors.New("File exists and is not a FIFO")
}
}

go d.stateLoop(STATE_READY)

d.fifostream = gofaxlib.NewFifoStream(d.fifoname)
go d.fifoLoop()

d.SetReady()
return &d, nil
}

func (d *Device) fifoLoop() {

for {
select {
case msg := <-d.fifostream.Messages():

if len(msg) == 0 {
continue
}
logger.Logger.Println(d.fifoname, "received message:", msg)

switch msg[0] {
case 'H': // Hello
d.SetReady()
case 'L': // Lock
d.SetLocked()
case 'S': // Set state
if len(msg) < 2 {
continue
}
switch msg[1] {
case 'R':
d.SetReady()
case 'B':
d.SetBusy("Sending facsimile", true)
}
default:
logger.Logger.Println("Unhandled message:", msg)
}

case err := <-d.fifostream.Errors():
logger.Logger.Printf("Error in stream for FIFO %s: %v", d.fifoname, err)
return
}
}

}

func (d *Device) stateLoop(state uint) {
for {
select {
case state = <-d.stateSet:
case d.stateGet <- state:
}
}
}

func (d *Device) handle(msg string) {
}

func (d *Device) WriteStatusFile(msg string) {
sfh, err := os.OpenFile(d.statusfile, os.O_RDWR|os.O_CREATE, 0666)
if err != nil {
logger.Logger.Print(err)
return
}

if err = syscall.Flock(int(sfh.Fd()), syscall.LOCK_EX); err != nil {
sfh.Close()
logger.Logger.Print(err)
return
}

// Truncate after acquiring lock!
sfh.Truncate(0)

if _, err := sfh.WriteString(msg); err != nil {
logger.Logger.Print(err)
return
}

if err = sfh.Close(); err != nil {
logger.Logger.Print(err)
return
}
}

func (d *Device) GetState() uint {
return <-d.stateGet
}

func (d *Device) SetReady() {
logger.Logger.Printf("Changing state of modem %v to READY", d.Name)
d.stateSet <- STATE_READY
gofaxlib.Faxq.ModemStatus(d.Name, "N")
d.WriteStatusFile("Running and idle")
gofaxlib.Faxq.ModemStatusReady(d.Name)
}

func (d *Device) SetBusy(msg string, outbound bool) {
logger.Logger.Printf("Changing state of modem %v to BUSY", d.Name)
d.stateSet <- STATE_BUSY

if outbound {
gofaxlib.Faxq.ModemStatus(d.Name, "U")
} else {
gofaxlib.Faxq.ModemStatus(d.Name, "B")
}

if msg == "" {
msg = "Busy"
}
d.WriteStatusFile(msg)
}

func (d *Device) SetDown() {
logger.Logger.Printf("Changing state of modem %v to DOWN", d.Name)
d.stateSet <- STATE_DOWN
gofaxlib.Faxq.ModemStatus(d.Name, "D")
d.WriteStatusFile("Down")
}

func (d *Device) SetLocked() {
logger.Logger.Printf("Changing state of modem %v to LOCKED", d.Name)
d.stateSet <- STATE_LOCKED
d.WriteStatusFile("Locked for sending")
}
22 changes: 14 additions & 8 deletions src/gofaxd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"flag"
"fmt"
"gofaxlib"
//"gofaxlib/device"
"gofaxlib/logger"
"log"
"os"
Expand All @@ -16,6 +15,7 @@ import (
const (
DEFAULT_CONFIGFILE = "/etc/gofax.conf"
PRODUCT_NAME = "GOfax.IP"
MODEM_PREFIX = "freeswitch"
)

var (
Expand All @@ -27,6 +27,8 @@ var (
// Version can be set at build time using:
// -ldflags "-X main.version 0.42"
version string

devmanager *manager
)

func init() {
Expand Down Expand Up @@ -56,28 +58,32 @@ func main() {
log.Fatal(err)
}

// Shut down receiving lines when killed
sigchan := make(chan os.Signal, 1)
signal.Notify(sigchan, syscall.SIGTERM, syscall.SIGINT)

// Start modem device manager
var err error
devmanager, err = NewManager(MODEM_PREFIX, gofaxlib.Config.Hylafax.Modems)
if err != nil {
logger.Logger.Fatal(err)
}

// Start event socket server to handle incoming calls
server := NewEventSocketServer()
server.Start()

// Block until something happens
select {
case err := <-server.Errors():
logger.Logger.Print(err)
log.Fatal(err)
logger.Logger.Fatal(err)
case sig := <-sigchan:
logger.Logger.Print("Received ", sig, ", killing all channels")
server.Kill()
devmanager.SetAllDown()
time.Sleep(3 * time.Second)
logger.Logger.Print("Terminating")
os.Exit(1)
}

// _, err := device.NewDevice("freeswitch1")
// if err != nil {
// log.Fatal(err)
// }

}
46 changes: 46 additions & 0 deletions src/gofaxd/manager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package main

import (
"errors"
"fmt"
)

type manager struct {
devices []*Device
}

func NewManager(nameprefix string, count uint) (*manager, error) {
var err error

m := &manager{
devices: make([]*Device, count),
}

for i := uint(0); i < count; i++ {
m.devices[i], err = NewDevice(fmt.Sprintf("%v%v", nameprefix, i))
if err != nil {
m.SetAllDown()
return nil, err
}
}

return m, nil
}

func (m *manager) SetAllDown() {
for _, d := range m.devices {
if d != nil {
d.SetDown()
}
}
}

func (m *manager) FindDevice(msg string) (*Device, error) {
for _, d := range m.devices {
if d.GetState() == STATE_READY {
d.SetBusy(msg, false)
return d, nil
}
}
return nil, errors.New("No available modem found.")
}
41 changes: 40 additions & 1 deletion src/gofaxd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const (
RECVQ_FILE_FORMAT = "fax%08d.tif"
RECVQ_DIR = "recvq"
DEFAULT_FAXRCVD_CMD = "bin/faxrcvd"
DEFAULT_DEVICE = "freeswitch"
)

type EventSocketServer struct {
Expand Down Expand Up @@ -112,6 +113,18 @@ func (e *EventSocketServer) handler(c *eventsocket.Connection) {

}

var device *Device
if gofaxlib.Config.Gofaxd.AllocateOutboundDevices {
// Find free device
device, err := devmanager.FindDevice(fmt.Sprintf("Receiving facsimile"))
if err != nil {
logger.Logger.Println(err)
c.Execute("respond", "404", true)
c.Send("exit")
}
defer device.SetReady()
}

// Fetch commid and log file name
commseq, err := gofaxlib.GetSeqFor(LOG_DIR)
if err != nil {
Expand All @@ -135,6 +148,14 @@ func (e *EventSocketServer) handler(c *eventsocket.Connection) {

logfunc(fmt.Sprintf("Accepting call to %v from %v <%v> with commid %v", recipient, cidname, cidnum, commid))

if device != nil {
// Notify faxq
gofaxlib.Faxq.ModemStatus(device.Name, "I"+commid)
gofaxlib.Faxq.ReceiveStatus(device.Name, "B")
gofaxlib.Faxq.ReceiveStatus(device.Name, "S")
defer gofaxlib.Faxq.ReceiveStatus(device.Name, "E")
}

// Start interacting with the caller

if gofaxlib.Config.Gofaxd.Answerafter != 0 {
Expand Down Expand Up @@ -168,6 +189,8 @@ func (e *EventSocketServer) handler(c *eventsocket.Connection) {
result := gofaxlib.NewFaxResult(channel_uuid, logfunc)
es := gofaxlib.NewEventStream(c)

pages := result.TransferredPages

EventLoop:
for {
select {
Expand All @@ -178,6 +201,12 @@ EventLoop:
break EventLoop
}
result.AddEvent(ev)
if pages != result.TransferredPages {
pages = result.TransferredPages
if device != nil {
gofaxlib.Faxq.ReceiveStatus(device.Name, "P")
}
}
case err := <-es.Errors():
if err.Error() == "EOF" {
logfunc("Event socket client disconnected")
Expand All @@ -193,6 +222,9 @@ EventLoop:
}
}

if device != nil {
gofaxlib.Faxq.ReceiveStatus(device.Name, "D")
}
logfunc(fmt.Sprintf("Success: %v, Hangup Cause: %v, Result: %v", result.Success, result.Hangupcause, result.ResultText))

// Process received file
Expand All @@ -205,7 +237,14 @@ EventLoop:
errmsg = result.ResultText
}

cmd := exec.Command(rcvdcmd, filename, "freeswitch1", commid, errmsg, cidnum, cidname, recipient)
var used_device string
if device != nil {
used_device = device.Name
} else {
used_device = DEFAULT_DEVICE
}

cmd := exec.Command(rcvdcmd, filename, used_device, commid, errmsg, cidnum, cidname, recipient)
logfunc("Calling", cmd.Path, cmd.Args)
if output, err := cmd.CombinedOutput(); err != nil {
logfunc(cmd.Path, "ended with", err)
Expand Down
Loading

0 comments on commit 25911b9

Please sign in to comment.