Add a Unix socket connection

Unix-domain sockets have less overhead than TCP.
This commit is contained in:
Matthias Urlichs 2023-12-05 18:07:24 +01:00 committed by root
parent c33738a320
commit 2bf2115ab5
7 changed files with 208 additions and 13 deletions

View File

@ -25,6 +25,12 @@ default may be overridden when pigpio starts by the <a href=
call.&nbsp; The pigpio daemon uses this function to provide an
option to change the port number.<br>
<br>
pigpio also listens for connections on "/var/run/pigpio.sock" by
default.&nbsp; This default may be overridden when pigpio starts
by the <a href= "cif.html#gpioCfgSocketPath">gpioCfgSocketPath</a>
function call.&nbsp; The pigpio daemon uses this function to provide an
option to change the socket file name.<br>
<br>
The pigs utility is an example of using the socket interface from
C.<span style="font-weight: bold;"><br></span>
<h3><a name="Request" id="Request"></a>Request</h3>

View File

@ -567,6 +567,7 @@ static errInfo_t errInfo[]=
{PI_CMD_INTERRUPTED , "command interrupted, Python"},
{PI_NOT_ON_BCM2711 , "not available on BCM2711"},
{PI_ONLY_ON_BCM2711 , "only available on BCM2711"},
{PI_BAD_SOCKET_PATH , "socket path empty"},
};

View File

@ -706,6 +706,8 @@ gpioCfgInterfaces Configure user interfaces
.br
gpioCfgSocketPort Configure socket port
.br
gpioCfgSocketPath Configure socket path
.br
gpioCfgMemAlloc Configure DMA memory allocation mode
.br
gpioCfgNetAddr Configure allowed network addresses
@ -7802,6 +7804,30 @@ port: 1024-32000
.br
The default setting is to use port 8888.
.IP "\fBint gpioCfgSocketPath(const char*)\fP"
.IP "" 4
Configures pigpio to use the specified Unix socket.
.br
.br
This function is only effective if called before \fBgpioInitialise\fP.
.br
.br
.EX
path: path to the socket.
.br
.EE
.br
.br
The default is "/var/run/pigpio.sock".
.IP "\fBint gpioCfgInterfaces(unsigned ifFlags)\fP"
.IP "" 4
Configures pigpio support of the fifo and socket interfaces.
@ -8945,6 +8971,8 @@ These functions are only effective if called before \fBgpioInitialise\fP.
.br
\fBgpioCfgSocketPort\fP
.br
\fBgpioCfgSocketPath\fP
.br
\fBgpioCfgMemAlloc\fP
.br
@ -11040,6 +11068,8 @@ A 16-bit word value.
.br
#define PI_ONLY_ON_BCM2711 -146 // only available on BCM2711
.br
#define PI_BAD_SOCKET_PORT -147 // socket path empty
.br
.br
#define PI_PIGIF_ERR_0 -2000

116
pigpio.c
View File

@ -58,6 +58,7 @@ For more information, please refer to <http://unlicense.org/>
#include <sys/socket.h>
#include <sys/sysmacros.h>
#include <netinet/tcp.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <fnmatch.h>
@ -1096,6 +1097,7 @@ typedef struct
unsigned DMAprimaryChannel;
unsigned DMAsecondaryChannel;
unsigned socketPort;
const char * socketPath;
unsigned ifFlags;
unsigned memAllocMode;
unsigned dbgLevel;
@ -1292,6 +1294,7 @@ static volatile int runState = PI_STARTING;
static int pthAlertRunning = PI_THREAD_NONE;
static int pthFifoRunning = PI_THREAD_NONE;
static int pthSocketRunning = PI_THREAD_NONE;
static int pthUnixRunning = PI_THREAD_NONE;
static gpioAlert_t gpioAlert [PI_MAX_USER_GPIO+1];
@ -1328,6 +1331,7 @@ static FILE * outFifo = NULL;
static int fdLock = -1;
static int fdMem = -1;
static int fdSock = -1;
static int fdUnix = -1;
static int fdPmap = -1;
static int fdMbox = -1;
@ -1369,6 +1373,7 @@ static volatile gpioCfg_t gpioCfg =
PI_DEFAULT_DMA_NOT_SET, /* primary DMA */
PI_DEFAULT_DMA_NOT_SET, /* secondary DMA */
PI_DEFAULT_SOCKET_PORT,
PI_DEFAULT_SOCKET_PATH,
PI_DEFAULT_IF_FLAGS,
PI_DEFAULT_MEM_ALLOC_MODE,
0, /* dbgLevel */
@ -1384,6 +1389,7 @@ static unsigned bufferCycles; /* number of cycles */
static pthread_t pthAlert;
static pthread_t pthFifo;
static pthread_t pthSocket;
static pthread_t pthUnix;
static uint32_t spi_dummy;
@ -7260,6 +7266,61 @@ static void * pthSocketThread(void *x)
return 0;
}
static void * pthUnixThread(void *x)
{
int fdC=0, c, *sock;
struct sockaddr_storage client;
pthread_attr_t attr;
if (pthread_attr_init(&attr))
SOFT_ERROR((void*)PI_INIT_FAILED,
"pthread_attr_init failed (%m)");
if (pthread_attr_setstacksize(&attr, STACK_SIZE))
SOFT_ERROR((void*)PI_INIT_FAILED,
"pthread_attr_setstacksize failed (%m)");
if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED))
SOFT_ERROR((void*)PI_INIT_FAILED,
"pthread_attr_setdetachstate failed (%m)");
/* fdUnix opened in gpioInitialise so that we can treat
failure to bind as fatal. */
listen(fdUnix, 100);
c = sizeof(client);
/* don't start until DMA started */
spinWhileStarting();
while (fdC >= 0)
{
pthread_t thr;
fdC = accept(fdUnix, (struct sockaddr *)&client, (socklen_t*)&c);
closeOrphanedNotifications(-1, fdC);
DBG(DBG_USER, "Connection accepted on socket %d", fdC);
sock = malloc(sizeof(int));
*sock = fdC;
if (pthread_create
(&thr, &attr, pthSocketThreadHandler, (void*) sock) < 0)
SOFT_ERROR((void*)PI_INIT_FAILED,
"socket pthread_create failed (%m)");
}
if (fdC < 0)
SOFT_ERROR((void*)PI_INIT_FAILED, "accept failed (%m)");
return 0;
}
/* ======================================================================= */
static void initCheckLockFile(void)
@ -7969,6 +8030,7 @@ static void initClearGlobals(void)
pthAlertRunning = PI_THREAD_NONE;
pthFifoRunning = PI_THREAD_NONE;
pthSocketRunning = PI_THREAD_NONE;
pthUnixRunning = PI_THREAD_NONE;
wfc[0] = 0;
wfc[1] = 0;
@ -8051,6 +8113,7 @@ static void initClearGlobals(void)
fdLock = -1;
fdMem = -1;
fdSock = -1;
fdUnix = -1;
dmaMboxBlk = MAP_FAILED;
dmaPMapBlk = MAP_FAILED;
@ -8120,6 +8183,13 @@ static void initReleaseResources(void)
pthSocketRunning = PI_THREAD_NONE;
}
if (pthUnixRunning != PI_THREAD_NONE)
{
pthread_cancel(pthUnix);
pthread_join(pthUnix, NULL);
pthUnixRunning = PI_THREAD_NONE;
}
/* release mmap'd memory */
if (auxReg != MAP_FAILED) munmap((void *)auxReg, AUX_LEN);
@ -8224,6 +8294,12 @@ static void initReleaseResources(void)
fdSock = -1;
}
if (fdUnix != -1)
{
close(fdUnix);
fdUnix = -1;
}
if (fdPmap != -1)
{
close(fdPmap);
@ -8248,7 +8324,9 @@ int initInitialise(void)
unsigned rev, model;
struct sockaddr_in server;
struct sockaddr_in6 server6;
struct sockaddr_un serverU;
char * portStr;
const char * sockStr;
unsigned port;
struct sched_param param;
pthread_attr_t pthAttr;
@ -8368,6 +8446,29 @@ int initInitialise(void)
pthFifoRunning = PI_THREAD_STARTED;
}
if (!(gpioCfg.ifFlags & PI_DISABLE_UNIX_IF))
{
sockStr = getenv(PI_ENVSOCK);
if (! sockStr) sockStr = gpioCfg.socketPath;
fdUnix = socket(AF_UNIX, SOCK_STREAM , 0);
if (fdUnix != -1)
{
bzero((char *)&serverU, sizeof(serverU));
serverU.sun_family = AF_UNIX;
strncpy (serverU.sun_path, sockStr, sizeof (serverU.sun_path) - 1);
if (bind(fdUnix,(struct sockaddr *)&serverU, sizeof(serverU)) < 0)
SOFT_ERROR(PI_INIT_FAILED, "bind to socket '%s' failed (%m)", sockStr);
}
if (pthread_create(&pthUnix, &pthAttr, pthUnixThread, &i))
SOFT_ERROR(PI_INIT_FAILED, "pthread_create unix failed (%m)");
pthUnixRunning = PI_THREAD_STARTED;
}
if (!(gpioCfg.ifFlags & PI_DISABLE_SOCK_IF))
{
portStr = getenv(PI_ENVPORT);
@ -13972,6 +14073,21 @@ int gpioCfgSocketPort(unsigned port)
}
int gpioCfgSocketPath(const char *path)
{
DBG(DBG_USER, "path=%s", path);
CHECK_NOT_INITED;
if (!path || !*path)
SOFT_ERROR(PI_BAD_SOCKET_PATH, "bad path");
gpioCfg.socketPath = path;
return 0;
}
/* ----------------------------------------------------------------------- */
int gpioCfgMemAlloc(unsigned memAllocMode)

View File

@ -385,6 +385,7 @@ gpioCfgDMAchannels Configure the DMA channels
gpioCfgPermissions Configure the GPIO access permissions
gpioCfgInterfaces Configure user interfaces
gpioCfgSocketPort Configure socket port
gpioCfgSocketPath Configure Unix socket path
gpioCfgMemAlloc Configure DMA memory allocation mode
gpioCfgNetAddr Configure allowed network addresses
@ -415,6 +416,7 @@ OVERVIEW*/
#define PI_ENVPORT "PIGPIO_PORT"
#define PI_ENVADDR "PIGPIO_ADDR"
#define PI_ENVSOCK "PIGPIO_SOCKET"
#define PI_LOCKFILE "/var/run/pigpio.pid"
@ -890,6 +892,7 @@ typedef void *(gpioThreadFunc_t) (void *);
#define PI_DISABLE_SOCK_IF 2
#define PI_LOCALHOST_SOCK_IF 4
#define PI_DISABLE_ALERT 8
#define PI_DISABLE_UNIX_IF 16
/* memAllocMode */
@ -4919,6 +4922,21 @@ The default setting is to use port 8888.
D*/
/*F*/
int gpioCfgSocketPath(const char * path);
/*D
Configures pigpio to use the specified Unix socket.
This function is only effective if called before [*gpioInitialise*].
. .
port: path to the socket file.
. .
The default is "/var/run/pigpio.sock".
D*/
/*F*/
int gpioCfgInterfaces(unsigned ifFlags);
/*D
@ -5538,6 +5556,7 @@ These functions are only effective if called before [*gpioInitialise*].
[*gpioCfgPermissions*]
[*gpioCfgInterfaces*]
[*gpioCfgSocketPort*]
[*gpioCfgSocketPath*]
[*gpioCfgMemAlloc*]
gpioGetSamplesFunc_t::
@ -6528,6 +6547,7 @@ after this command is issued.
#define PI_CMD_INTERRUPTED -144 // Used by Python
#define PI_NOT_ON_BCM2711 -145 // not available on BCM2711
#define PI_ONLY_ON_BCM2711 -146 // only available on BCM2711
#define PI_BAD_SOCKET_PATH -147 // socket path empty
#define PI_PIGIF_ERR_0 -2000
#define PI_PIGIF_ERR_99 -2099
@ -6550,6 +6570,7 @@ after this command is issued.
#define PI_DEFAULT_DMA_PRIMARY_CH_2711 7
#define PI_DEFAULT_DMA_SECONDARY_CH_2711 6
#define PI_DEFAULT_DMA_NOT_SET 15
#define PI_DEFAULT_SOCKET_PATH "/var/run/pigpio.sock"
#define PI_DEFAULT_SOCKET_PORT 8888
#define PI_DEFAULT_SOCKET_PORT_STR "8888"
#define PI_DEFAULT_SOCKET_ADDR_STR "localhost"

View File

@ -723,6 +723,7 @@ PI_BAD_EVENT_ID =-143
PI_CMD_INTERRUPTED =-144
PI_NOT_ON_BCM2711 =-145
PI_ONLY_ON_BCM2711 =-146
_PI_BAD_SOCKET_PATH =-147
# pigpio error text
@ -871,6 +872,7 @@ _errors=[
[PI_CMD_INTERRUPTED , "pigpio command interrupted"],
[PI_NOT_ON_BCM2711 , "not available on BCM2711"],
[PI_ONLY_ON_BCM2711 , "only available on BCM2711"],
[_PI_BAD_SOCKET_PATH , "socket path empty"],
]
_except_a = "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n{}"
@ -5177,6 +5179,7 @@ class pi():
def __init__(self,
host = os.getenv("PIGPIO_ADDR", 'localhost'),
port = os.getenv("PIGPIO_PORT", 8888),
sock = os.getenv("PIGPIO_SOCKET", None),
show_errors = True):
"""
Grants access to a Pi's GPIO.
@ -5201,6 +5204,7 @@ class pi():
pi = pigio.pi() # use defaults
pi = pigpio.pi('mypi') # specify host, default port
pi = pigpio.pi('mypi', 7777) # specify host and port
pi = pigpio.pi(sock='/run/pigpio.sock') # specify a Unix socket
pi = pigpio.pi() # exit script if no connection
if not pi.connected:
@ -5212,19 +5216,24 @@ class pi():
self.sl = _socklock()
self._notify = None
port = int(port)
if host == '':
host = "localhost"
self._host = host
self._port = port
self._sock = sock
try:
self.sl.s = socket.create_connection((host, port), None)
if sock:
self.sl.s = socket.socket(family=socket.AF_UNIX, type=socket.SOCK_STREAM)
self.sl.s.connect(sock)
else:
port = int(port)
# Disable the Nagle algorithm.
self.sl.s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
if host == '':
host = "localhost"
self._host = host
self._port = port
self.sl.s = socket.create_connection((host, port), None)
# Disable the Nagle algorithm.
self.sl.s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
self._notify = _callback_thread(self.sl, host, port)
@ -5264,7 +5273,9 @@ class pi():
print(_except_z)
def __repr__(self):
return "<pipio.pi host={} port={}>".format(self._host, self._port)
if self._sock is None:
return "<pipio.pi host={} port={}>".format(self._host, self._port)
return "<pipio.pi sock={}>".format(self._sock)
def stop(self):
"""Release pigpio resources.

View File

@ -59,6 +59,7 @@ static int foreground = PI_DEFAULT_FOREGROUND;
static unsigned DMAprimaryChannel = PI_DEFAULT_DMA_NOT_SET;
static unsigned DMAsecondaryChannel = PI_DEFAULT_DMA_NOT_SET;
static unsigned socketPort = PI_DEFAULT_SOCKET_PORT;
static char * socketPath = PI_DEFAULT_SOCKET_PATH;
static unsigned memAllocMode = PI_DEFAULT_MEM_ALLOC_MODE;
static uint64_t updateMask = -1;
@ -106,6 +107,7 @@ void usage()
" -n IP addr, allow address, name or dotted, default allow all\n" \
" -p value, socket port, 1024-32000, default 8888\n" \
" -s value, sample rate, 1, 2, 4, 5, 8, or 10, default 5\n" \
" -S path, Unix socket file, default /var/run/pigpio.sock\n" \
" -t value, clock peripheral, 0=PWM 1=PCM, default PCM\n" \
" -v, -V, display pigpio version and exit\n" \
" -x mask, GPIO which may be updated, default board GPIO\n" \
@ -163,7 +165,7 @@ static void initOpts(int argc, char *argv[])
uint32_t addr;
int64_t mask;
while ((opt = getopt(argc, argv, "a:b:c:d:e:fgkln:mp:s:t:x:vV")) != -1)
while ((opt = getopt(argc, argv, "a:b:c:d:e:fgkln:mp:s:S:t:x:vV")) != -1)
{
switch (opt)
{
@ -256,6 +258,12 @@ static void initOpts(int argc, char *argv[])
}
break;
case 'S':
if (!*optarg)
fatal("invalid -S option (empty string)");
socketPath = optarg;
break;
case 't':
i = getNum(optarg, &err);
if ((i >= PI_CLOCK_PWM) && (i <= PI_CLOCK_PCM))
@ -358,6 +366,8 @@ int main(int argc, char **argv)
gpioCfgSocketPort(socketPort);
gpioCfgSocketPath(socketPath);
gpioCfgMemAlloc(memAllocMode);
if (updateMaskSet) gpioCfgPermissions(updateMask);