I'm trying to learn how to use epoll() for tcp server application, 'cause i'm expecting many connections.
i tried checking samples and tutorials, they always recommend using/setting sockets that are added in epoll() to be NON-BLOCKING sockets. why?
For level-triggered epoll, nonblocking sockets can help to minimize epoll_wait() calls, its an optimization issue.
For edge-triggered epoll, you MUST use nonblocking sockets AND call read() or write() until they return EWOULDBLOCK. If you don't, you can miss kernel notifications.
You can find a detailed answer here: https://eklitzke.org/blocking-io-nonblocking-io-and-epoll
It's a good question and not duplicated. Recently I also find a tutorial using nonblocking socket in select (select is level-triggered only), which causes me to think.
The question is:
Why using nonblocking IO or set fd to nonblicking, in level-triggered epoll, select or other similar interfaces?
There are in fact very solid reasons for this case.
Cite from the book The Linux Programming Interface :
63.1.2 Employing Nonblocking I/O with Alternative I/O Models
Nonblocking I/O (the O_NONBLOCK flag) is often used in conjunction
with the I/O models described in this chapter. Some examples of why
this can be useful are the following:
As explained in the previous section, nonblocking I/O is usually employed in conjunction with I/O models that provide edge-triggered
notification of I/O events.
If multiple processes (or threads) are performing I/O on the same open file descriptions, then, from a particular process’s point of
view, a descriptor’s readiness may change between the time the
descriptor was notified as being ready and the time of the subsequent
I/O call. Consequently, a blocking I/O call could block, thus
preventing the process from monitoring other file descriptors. (This
can occur for all of the I/O models that we describe in this chapter,
regardless of whether they employ level-triggered or edge-triggered
notification.)
Even after a level-triggered API such as select() or poll() informs us that a file descriptor for a stream socket is ready for writing, if
we write a large enough block of data in a single write() or send(),
then the call will nevertheless block.
In rare cases, level-triggered APIs such as select() and poll() can return spurious readiness notifications—they can falsely inform us
that a file descriptor is ready. This could be caused by a kernel bug
or be expected behavior in an uncommon scenario.
First, let's check case #2: "If multiple processes (or threads) are performing I/O on the same open file descriptions...".
Read this code from libevent introduction, http://www.wangafu.net/~nickm/libevent-book/01_intro.html .
/* For sockaddr_in */
#include <netinet/in.h>
/* For socket functions */
#include <sys/socket.h>
/* For fcntl */
#include <fcntl.h>
/* for select */
#include <sys/select.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#define MAX_LINE 16384
char
rot13_char(char c)
{
/* We don't want to use isalpha here; setting the locale would change
* which characters are considered alphabetical. */
if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M'))
return c + 13;
else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z'))
return c - 13;
else
return c;
}
struct fd_state {
char buffer[MAX_LINE];
size_t buffer_used;
int writing;
size_t n_written;
size_t write_upto;
};
struct fd_state *
alloc_fd_state(void)
{
struct fd_state *state = malloc(sizeof(struct fd_state));
if (!state)
return NULL;
state->buffer_used = state->n_written = state->writing =
state->write_upto = 0;
return state;
}
void
free_fd_state(struct fd_state *state)
{
free(state);
}
void
make_nonblocking(int fd)
{
fcntl(fd, F_SETFL, O_NONBLOCK);
}
int
do_read(int fd, struct fd_state *state)
{
char buf[1024];
int i;
ssize_t result;
while (1) {
result = recv(fd, buf, sizeof(buf), 0);
if (result <= 0)
break;
for (i=0; i < result; ++i) {
if (state->buffer_used < sizeof(state->buffer))
state->buffer[state->buffer_used++] = rot13_char(buf[i]);
if (buf[i] == '\n') {
state->writing = 1;
state->write_upto = state->buffer_used;
}
}
}
if (result == 0) {
return 1;
} else if (result < 0) {
if (errno == EAGAIN)
return 0;
return -1;
}
return 0;
}
int
do_write(int fd, struct fd_state *state)
{
while (state->n_written < state->write_upto) {
ssize_t result = send(fd, state->buffer + state->n_written,
state->write_upto - state->n_written, 0);
if (result < 0) {
if (errno == EAGAIN)
return 0;
return -1;
}
assert(result != 0);
state->n_written += result;
}
if (state->n_written == state->buffer_used)
state->n_written = state->write_upto = state->buffer_used = 0;
state->writing = 0;
return 0;
}
void
run(void)
{
int listener;
struct fd_state *state[FD_SETSIZE];
struct sockaddr_in sin;
int i, maxfd;
fd_set readset, writeset, exset;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0;
sin.sin_port = htons(40713);
for (i = 0; i < FD_SETSIZE; ++i)
state[i] = NULL;
listener = socket(AF_INET, SOCK_STREAM, 0);
make_nonblocking(listener);
#ifndef WIN32
{
int one = 1;
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
}
#endif
if (bind(listener, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
perror("bind");
return;
}
if (listen(listener, 16)<0) {
perror("listen");
return;
}
FD_ZERO(&readset);
FD_ZERO(&writeset);
FD_ZERO(&exset);
while (1) {
maxfd = listener;
FD_ZERO(&readset);
FD_ZERO(&writeset);
FD_ZERO(&exset);
FD_SET(listener, &readset);
for (i=0; i < FD_SETSIZE; ++i) {
if (state[i]) {
if (i > maxfd)
maxfd = i;
FD_SET(i, &readset);
if (state[i]->writing) {
FD_SET(i, &writeset);
}
}
}
if (select(maxfd+1, &readset, &writeset, &exset, NULL) < 0) {
perror("select");
return;
}
if (FD_ISSET(listener, &readset)) {
struct sockaddr_storage ss;
socklen_t slen = sizeof(ss);
int fd = accept(listener, (struct sockaddr*)&ss, &slen);
if (fd < 0) {
perror("accept");
} else if (fd > FD_SETSIZE) {
close(fd);
} else {
make_nonblocking(fd);
state[fd] = alloc_fd_state();
assert(state[fd]);/*XXX*/
}
}
for (i=0; i < maxfd+1; ++i) {
int r = 0;
if (i == listener)
continue;
if (FD_ISSET(i, &readset)) {
r = do_read(i, state[i]);
}
if (r == 0 && FD_ISSET(i, &writeset)) {
r = do_write(i, state[i]);
}
if (r) {
free_fd_state(state[i]);
state[i] = NULL;
close(i);
}
}
}
}
int
main(int c, char **v)
{
setvbuf(stdout, NULL, _IONBF, 0);
run();
return 0;
}
This is not an example of multiple processes (or threads) performing I/O on the same open file descriptions, but it demostrates the same idea.
In the do_read function, it uses recv in side a while(1) to read as many bytes as possible, but 1024 bytes for each recv. I guess this is a typical pattern.
So you need nonblocking here, otherwise recv will eventually block when there's no data in network input.
For #3, if you write too much data in a blocking socket and there's no enough buffer. send will block until all data are sent. And it could block for long enough time if there's no enough space in the send buffer. More details check https://stackoverflow.com/a/74172742/5983841 .
Related
I have a poll() loop with a small socket communication, I want to start an other program by system() or exec() and I need the the return value of the system()/exec() but I don't want to stop the main loop while the child process is running so I thought I start it in a thread but I am not sure how to set up the pollfd to catch the thread when it is done, I am using c/c++
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <poll.h>
#include <iostream>
#include <string>
#include <thread>
#include <future>
#define SOCKET_NAME "/tmp/9Lq7BNBnBycd6nxy.socket"
int runProgram(const std::string &programName, const std::string &fileName) {
return system((programName + " " + fileName).c_str());
}
int main(int argc, char *argv[]) {
struct sockaddr_un server;
int sock;
char buf[1024];
unlink(SOCKET_NAME);
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock == -1){
perror("socket");
exit(EXIT_FAILURE);
}
memset(&server, 0, sizeof(struct sockaddr_un));
server.sun_family = AF_UNIX;
strncpy(server.sun_path, SOCKET_NAME, sizeof(server.sun_path) - 1);
if (bind(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
if (listen(sock, 3) < -1) {
perror("listen");
exit(EXIT_FAILURE);
}
struct pollfd fds[2];
fds[0].fd = sock;
fds[0].events = POLLIN;
std::future<int> ret = std::async(&runProgram, "cat", "test.txt");
while (true) {
poll(fds, 2, -1);
if(fds[0].revents & POLLIN) {
int new_sd = accept(fds[0].fd, NULL, NULL);
if (new_sd < 0) {
perror("accept");
}
fds[1].fd = new_sd;
}
if (fds[0].revents & POLLIN) {
int rv = recv(fds[1].fd, buf, 1024, 0);
if (rv < 0)
perror("recv");
else if (rv == 0) {
printf("disconnet\n");
close(fds[1].fd);
} else {
printf("%s\n", buf);
send(fds[1].fd, buf, 1024, 0);
}
memset(buf, 0, 1024);
}
}
close(sock);
return(EXIT_SUCCESS);
}
So I want to add one more to the pollfd (fds[ret.get()]) and get a POLLIN on fds[2] when my thread is done and I can get the return value (ret.get()), here I used an exaple command cat but in my final code the command would need mach more time so I cant wait for that to finish
The simplest solution is to create an anonymous pipe (or, since you say that you are on Linux, an eventfd) and write data to one end of the pipe in the runProgram function once the call to system returns. You can then include the read end of the pipe in the set of file descriptors that you are polling.
int process_eventfd = eventfd(0, EFD_CLOEXEC);
if (process_eventfd == -1) exit(1); // change this to handle appropriately
struct pollfd fds[3];
fds[0].fd = sock;
fds[0].events = POLLIN;
fds[1].fd = process_eventfd;
fds[1].events = POLLIN;
// use fds[2] instead of fds[1] for your socket connection, etc.
You can add the eventfd number as an argument to runProgram. It should now look something like:
int runProgram(const std::string &programName, const std::string &fileName, int process_eventfd) {
return system((programName + " " + fileName).c_str());
uint64_t value = 1;
write(process_eventfd, &value, 8);
}
By the way, your current program has a bug: you always pass 2 as the number of file descriptors to poll, even before you have set up the second file descriptor in the array. You should only pass the number of valid descriptors actually present in the array.
However, if you don't need to use system and can use exec, there is no need to create another thread; just perform the following steps:
Mask (but don't ignore) the SIGCHLD signal. (You may need to set up a signal handler, even if it does nothing; I can't remember if this is true for Linux or not).
Create your external process via fork/exec
Use ppoll rather than poll, and include SIGCHLD in the signals to be enabled
If the ppoll call returns an EINTR error, use waitpid to obtain the child status
The child process will run in parallel to your program.
I am writing a single chat program with GUI. I wanted to write a server that would accept many clients. Every client can connect successfuly. But there is a strange problem with sending and receiving data. I use select() and a thread to handle many sockets at the same time. If a client sends some data to server, it will receive it and send it back to that client (the client is especially written without "prediction"). But the server won't send it further to another clients (like every client had its own private conversation with the server). Here's my code:
// this is rewritten from the Beej's tutorial with a little and insignificant changes
/* in the thread */
fd_set mainfd;
fd_set readfd;
// sin-size, newfd, maxfd - int
while(TRUE)
{
readfd = mainfd;
if(select(maxfd+1, &readfd, NULL, NULL, NULL) == -1)
{
MessageBoxA(NULL, "Error while trying to accept incoming connections (select)", "Error", 16);
itoa(GetLastError(), buf, 10);
MessageBoxA(NULL, buf, buf, 0);
break;
}
for(int i = 0; i <= maxfd; i++)
{
char* psr;
char srMsg[256];
if(FD_ISSET(i, &readfd))
{
if(i == mainSocket)
{
sin_size = sizeof(their_addr);
newfd = accept(mainSocket, (struct sockaddr*)&their_addr, &sin_size);
if(newfd == SOCKET_ERROR)
{
AddTextToEdit(hStaticChat, "* Error: couldn't accept incoming connection.", TRUE);
}
else
{
FD_SET(newfd, &mainfd);
if(newfd > maxfd)
{
maxfd = newfd;
}
}
}
else
{
len = recv(i, srMsg, 256, 0);
if(len == 0 || len == SOCKET_ERROR)
{
AddTextToEdit(hStaticChat, "* Client has disconnected", TRUE);
close(i);
FD_CLR(i, &mainfd);
}
else
{
AddTextToEdit(hStaticChat, srMsg, TRUE);
for(int j = 0; j <= maxfd; j++)
{
if(FD_ISSET(j, &readfd))
{
send(j, srMsg, len, 0);
}
}
}
}
}
}
}
You are only sending data to the clients whos fd is in readfd, that is, only to that one which just communicated to you. Try to test FD_ISSET(j, mainfd) instead.
This code is not valid under WinSock. Windows does not deal with sockets using integer file descriptors like other platforms do. Sockets are represented using actual kernel objects instead, so you can't use loop counters as socket handles and such. There is also API differences (closesocket() instead of close(), maxfd is ignored by select(), FD_XXX() expect SOCKET handles instead of int, etc).
On Windows, you need to use something more like this instead:
fd_set mainfd;
SOCKET newfd;
int sin_size;
...
while(TRUE)
{
fd_set readfd = mainfd;
if (select(0, &readfd, NULL, NULL, NULL) == SOCKET_ERROR)
{
itoa(WSAGetLastError(), buf, 10);
MessageBoxA(NULL, "Error while trying to accept incoming connections (select)", "Error", 16);
MessageBoxA(NULL, buf, buf, 0);
break;
}
for(int i = 0; i < readfd.fd_count; i++)
{
if (readfd.fd_array[i] == mainSocket)
{
sin_size = sizeof(their_addr);
newfd = accept(mainSocket, (struct sockaddr*)&their_addr, &sin_size);
if (newfd == INVALID_SOCKET)
{
AddTextToEdit(hStaticChat, "* Error: couldn't accept incoming connection.", TRUE);
}
else
{
// Note that fd_set can only hold FD_SETSIZE (64) sockets as a time!
FD_SET(newfd, &mainfd);
}
}
else
{
char srMsg[257];
len = recv(readfd.fd_array[i], srMsg, 256, 0);
if (len < 1)
{
if (len == 0)
AddTextToEdit(hStaticChat, "* Client has disconnected", TRUE);
else
AddTextToEdit(hStaticChat, "* Error: couldn't read from a client connection.", TRUE);
closesocket(readfd.fd_array[i]);
FD_CLR(readfd.fd_array[i], &mainfd);
}
else
{
srMsg[len] = 0;
AddTextToEdit(hStaticChat, srMsg, TRUE);
for (int j = 0; j < mainfd.fd_count; j++)
{
if (mainfd.fd_array[i] != mainSocket)
send(mainfd.fd_array[j], srMsg, len, 0);
}
}
}
}
}
Can someone provide me an example of how to use select() to see if a client has closed the connection on a socket?
FYI. I'm using linux.
Thanks!
The below snippet first checks if the socket is marked readable (which it is when closed) and then if there's actually anything to be read.
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>
bool isclosed(int sock) {
fd_set rfd;
FD_ZERO(&rfd);
FD_SET(sock, &rfd);
timeval tv = { 0 };
select(sock+1, &rfd, 0, 0, &tv);
if (!FD_ISSET(sock, &rfd))
return false;
int n = 0;
ioctl(sock, FIONREAD, &n);
return n == 0;
}
You don't need to do a select() followed by ioctl(). You can instead do a non-blocking peek on the socket to see if it returns 0.
bool isclosed (int sock) {
char x;
interrupted:
ssize_t r = ::recv(sock, &x, 1, MSG_DONTWAIT|MSG_PEEK);
if (r < 0) {
switch (errno) {
case EINTR: goto interrupted;
case EAGAIN: break; /* empty rx queue */
case ETIMEDOUT: break; /* recv timeout */
case ENOTCONN: break; /* not connected yet */
default: throw(errno);
}
}
return r == 0;
}
If you get a read notification and the read returns 0 bytes the client has exited cleanly. If not you will get an exception notification.
Here is an example to checks if the socket is closed by peer WITHOUT reading form it in case of non-empty recv buffer.
#ifndef _GNU_SOURCE
#define _GNU_SOURCE /* FOR: POLLRDHUP define */
#endif
#include <poll.h>
bool connection_peer_closed(int fd)
{
struct pollfd pfd;
int ret;
if (fd < 0) {
return true;
}
pfd.fd = fd;
pfd.events = POLLRDHUP;
pfd.revents = 0;
ret = poll(&pfd, 1, 0);
if (ret > 0 && (pfd.revents & (POLLHUP | POLLERR | POLLRDHUP))) {
return true;
} else {
return false;
}
}
What would be your suggestion in order to create a single instance application, so that only one process is allowed to run at a time? File lock, mutex or what?
A good way is:
#include <sys/file.h>
#include <errno.h>
int pid_file = open("/var/run/whatever.pid", O_CREAT | O_RDWR, 0666);
int rc = flock(pid_file, LOCK_EX | LOCK_NB);
if(rc) {
if(EWOULDBLOCK == errno)
; // another instance is running
}
else {
// this is the first instance
}
Note that locking allows you to ignore stale pid files (i.e. you don't have to delete them). When the application terminates for any reason the OS releases the file lock for you.
Pid files are not terribly useful because they can be stale (the file exists but the process does not). Hence, the application executable itself can be locked instead of creating and locking a pid file.
A more advanced method is to create and bind a unix domain socket using a predefined socket name. Bind succeeds for the first instance of your application. Again, the OS unbinds the socket when the application terminates for any reason. When bind() fails another instance of the application can connect() and use this socket to pass its command line arguments to the first instance.
Here is a solution in C++. It uses the socket recommendation of Maxim. I like this solution better than the file based locking solution, because the file based one fails if the process crashes and does not delete the lock file. Another user will not be able to delete the file and lock it. The sockets are automatically deleted when the process exits.
Usage:
int main()
{
SingletonProcess singleton(5555); // pick a port number to use that is specific to this app
if (!singleton())
{
cerr << "process running already. See " << singleton.GetLockFileName() << endl;
return 1;
}
... rest of the app
}
Code:
#include <netinet/in.h>
class SingletonProcess
{
public:
SingletonProcess(uint16_t port0)
: socket_fd(-1)
, rc(1)
, port(port0)
{
}
~SingletonProcess()
{
if (socket_fd != -1)
{
close(socket_fd);
}
}
bool operator()()
{
if (socket_fd == -1 || rc)
{
socket_fd = -1;
rc = 1;
if ((socket_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
throw std::runtime_error(std::string("Could not create socket: ") + strerror(errno));
}
else
{
struct sockaddr_in name;
name.sin_family = AF_INET;
name.sin_port = htons (port);
name.sin_addr.s_addr = htonl (INADDR_ANY);
rc = bind (socket_fd, (struct sockaddr *) &name, sizeof (name));
}
}
return (socket_fd != -1 && rc == 0);
}
std::string GetLockFileName()
{
return "port " + std::to_string(port);
}
private:
int socket_fd = -1;
int rc;
uint16_t port;
};
For windows, a named kernel object (e.g. CreateEvent, CreateMutex). For unix, a pid-file - create a file and write your process ID to it.
You can create an "anonymous namespace" AF_UNIX socket. This is completely Linux-specific, but has the advantage that no filesystem actually has to exist.
Read the man page for unix(7) for more info.
Avoid file-based locking
It is always good to avoid a file based locking mechanism to implement the singleton instance of an application. The user can always rename the lock file to a different name and run the application again as follows:
mv lockfile.pid lockfile1.pid
Where lockfile.pid is the lock file based on which is checked for existence before running the application.
So, it is always preferable to use a locking scheme on object directly visible to only the kernel. So, anything which has to do with a file system is not reliable.
So the best option would be to bind to a inet socket. Note that unix domain sockets reside in the filesystem and are not reliable.
Alternatively, you can also do it using DBUS.
It's seems to not be mentioned - it is possible to create a mutex in shared memory but it needs to be marked as shared by attributes (not tested):
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_mutex_t *mutex = shmat(SHARED_MEMORY_ID, NULL, 0);
pthread_mutex_init(mutex, &attr);
There is also shared memory semaphores (but I failed to find out how to lock one):
int sem_id = semget(SHARED_MEMORY_KEY, 1, 0);
No one has mentioned it, but sem_open() creates a real named semaphore under modern POSIX-compliant OSes. If you give a semaphore an initial value of 1, it becomes a mutex (as long as it is strictly released only if a lock was successfully obtained).
With several sem_open()-based objects, you can create all of the common equivalent Windows named objects - named mutexes, named semaphores, and named events. Named events with "manual" set to true is a bit more difficult to emulate (it requires four semaphore objects to properly emulate CreateEvent(), SetEvent(), and ResetEvent()). Anyway, I digress.
Alternatively, there is named shared memory. You can initialize a pthread mutex with the "shared process" attribute in named shared memory and then all processes can safely access that mutex object after opening a handle to the shared memory with shm_open()/mmap(). sem_open() is easier if it is available for your platform (if it isn't, it should be for sanity's sake).
Regardless of the method you use, to test for a single instance of your application, use the trylock() variant of the wait function (e.g. sem_trywait()). If the process is the only one running, it will successfully lock the mutex. If it isn't, it will fail immediately.
Don't forget to unlock and close the mutex on application exit.
It will depend on which problem you want to avoid by forcing your application to have only one instance and the scope on which you consider instances.
For a daemon — the usual way is to have a /var/run/app.pid file.
For user application, I've had more problems with applications which prevented me to run them twice than with being able to run twice an application which shouldn't have been run so. So the answer on "why and on which scope" is very important and will probably bring answer specific on the why and the intended scope.
Here is a solution based on sem_open
/*
*compile with :
*gcc single.c -o single -pthread
*/
/*
* run multiple instance on 'single', and check the behavior
*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <unistd.h>
#include <errno.h>
#define SEM_NAME "/mysem_911"
int main()
{
sem_t *sem;
int rc;
sem = sem_open(SEM_NAME, O_CREAT, S_IRWXU, 1);
if(sem==SEM_FAILED){
printf("sem_open: failed errno:%d\n", errno);
}
rc=sem_trywait(sem);
if(rc == 0){
printf("Obtained lock !!!\n");
sleep(10);
//sem_post(sem);
sem_unlink(SEM_NAME);
}else{
printf("Lock not obtained\n");
}
}
One of the comments on a different answer says "I found sem_open() rather lacking". I am not sure about the specifics of what's lacking
Based on the hints in maxim's answer here is my POSIX solution of a dual-role daemon (i.e. a single application that can act as daemon and as a client communicating with that daemon). This scheme has the advantage of providing an elegant solution of the problem when the instance started first should be the daemon and all following executions should just load off the work at that daemon. It is a complete example but lacks a lot of stuff a real daemon should do (e.g. using syslog for logging and fork to put itself into background correctly, dropping privileges etc.), but it is already quite long and is fully working as is. I have only tested this on Linux so far but IIRC it should be all POSIX-compatible.
In the example the clients can send integers passed to them as first command line argument and parsed by atoi via the socket to the daemon which prints it to stdout. With this kind of sockets it is also possible to transfer arrays, structs and even file descriptors (see man 7 unix).
#include <stdio.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/un.h>
#define SOCKET_NAME "/tmp/exampled"
static int socket_fd = -1;
static bool isdaemon = false;
static bool run = true;
/* returns
* -1 on errors
* 0 on successful server bindings
* 1 on successful client connects
*/
int singleton_connect(const char *name) {
int len, tmpd;
struct sockaddr_un addr = {0};
if ((tmpd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
printf("Could not create socket: '%s'.\n", strerror(errno));
return -1;
}
/* fill in socket address structure */
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, name);
len = offsetof(struct sockaddr_un, sun_path) + strlen(name);
int ret;
unsigned int retries = 1;
do {
/* bind the name to the descriptor */
ret = bind(tmpd, (struct sockaddr *)&addr, len);
/* if this succeeds there was no daemon before */
if (ret == 0) {
socket_fd = tmpd;
isdaemon = true;
return 0;
} else {
if (errno == EADDRINUSE) {
ret = connect(tmpd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
if (ret != 0) {
if (errno == ECONNREFUSED) {
printf("Could not connect to socket - assuming daemon died.\n");
unlink(name);
continue;
}
printf("Could not connect to socket: '%s'.\n", strerror(errno));
continue;
}
printf("Daemon is already running.\n");
socket_fd = tmpd;
return 1;
}
printf("Could not bind to socket: '%s'.\n", strerror(errno));
continue;
}
} while (retries-- > 0);
printf("Could neither connect to an existing daemon nor become one.\n");
close(tmpd);
return -1;
}
static void cleanup(void) {
if (socket_fd >= 0) {
if (isdaemon) {
if (unlink(SOCKET_NAME) < 0)
printf("Could not remove FIFO.\n");
} else
close(socket_fd);
}
}
static void handler(int sig) {
run = false;
}
int main(int argc, char **argv) {
switch (singleton_connect(SOCKET_NAME)) {
case 0: { /* Daemon */
struct sigaction sa;
sa.sa_handler = &handler;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGINT, &sa, NULL) != 0 || sigaction(SIGQUIT, &sa, NULL) != 0 || sigaction(SIGTERM, &sa, NULL) != 0) {
printf("Could not set up signal handlers!\n");
cleanup();
return EXIT_FAILURE;
}
struct msghdr msg = {0};
struct iovec iovec;
int client_arg;
iovec.iov_base = &client_arg;
iovec.iov_len = sizeof(client_arg);
msg.msg_iov = &iovec;
msg.msg_iovlen = 1;
while (run) {
int ret = recvmsg(socket_fd, &msg, MSG_DONTWAIT);
if (ret != sizeof(client_arg)) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
printf("Error while accessing socket: %s\n", strerror(errno));
exit(1);
}
printf("No further client_args in socket.\n");
} else {
printf("received client_arg=%d\n", client_arg);
}
/* do daemon stuff */
sleep(1);
}
printf("Dropped out of daemon loop. Shutting down.\n");
cleanup();
return EXIT_FAILURE;
}
case 1: { /* Client */
if (argc < 2) {
printf("Usage: %s <int>\n", argv[0]);
return EXIT_FAILURE;
}
struct iovec iovec;
struct msghdr msg = {0};
int client_arg = atoi(argv[1]);
iovec.iov_base = &client_arg;
iovec.iov_len = sizeof(client_arg);
msg.msg_iov = &iovec;
msg.msg_iovlen = 1;
int ret = sendmsg(socket_fd, &msg, 0);
if (ret != sizeof(client_arg)) {
if (ret < 0)
printf("Could not send device address to daemon: '%s'!\n", strerror(errno));
else
printf("Could not send device address to daemon completely!\n");
cleanup();
return EXIT_FAILURE;
}
printf("Sent client_arg (%d) to daemon.\n", client_arg);
break;
}
default:
cleanup();
return EXIT_FAILURE;
}
cleanup();
return EXIT_SUCCESS;
}
All credits go to Mark Lakata. I merely did some very minor touch up only.
main.cpp
#include "singleton.hpp"
#include <iostream>
using namespace std;
int main()
{
SingletonProcess singleton(5555); // pick a port number to use that is specific to this app
if (!singleton())
{
cerr << "process running already. See " << singleton.GetLockFileName() << endl;
return 1;
}
// ... rest of the app
}
singleton.hpp
#include <netinet/in.h>
#include <unistd.h>
#include <cerrno>
#include <string>
#include <cstring>
#include <stdexcept>
using namespace std;
class SingletonProcess
{
public:
SingletonProcess(uint16_t port0)
: socket_fd(-1)
, rc(1)
, port(port0)
{
}
~SingletonProcess()
{
if (socket_fd != -1)
{
close(socket_fd);
}
}
bool operator()()
{
if (socket_fd == -1 || rc)
{
socket_fd = -1;
rc = 1;
if ((socket_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
throw std::runtime_error(std::string("Could not create socket: ") + strerror(errno));
}
else
{
struct sockaddr_in name;
name.sin_family = AF_INET;
name.sin_port = htons (port);
name.sin_addr.s_addr = htonl (INADDR_ANY);
rc = bind (socket_fd, (struct sockaddr *) &name, sizeof (name));
}
}
return (socket_fd != -1 && rc == 0);
}
std::string GetLockFileName()
{
return "port " + std::to_string(port);
}
private:
int socket_fd = -1;
int rc;
uint16_t port;
};
#include <windows.h>
int main(int argc, char *argv[])
{
// ensure only one running instance
HANDLE hMutexH`enter code here`andle = CreateMutex(NULL, TRUE, L"my.mutex.name");
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
return 0;
}
// rest of the program
ReleaseMutex(hMutexHandle);
CloseHandle(hMutexHandle);
return 0;
}
FROM: HERE
On Windows you could also create a shared data segment and use an interlocked function to test for the first occurence, e.g.
#include <Windows.h>
#include <stdio.h>
#include <conio.h>
#pragma data_seg("Shared")
volatile LONG lock = 0;
#pragma data_seg()
#pragma comment(linker, "/SECTION:Shared,RWS")
void main()
{
if (InterlockedExchange(&lock, 1) == 0)
printf("first\n");
else
printf("other\n");
getch();
}
I have just written one, and tested.
#define PID_FILE "/tmp/pidfile"
static void create_pidfile(void) {
int fd = open(PID_FILE, O_RDWR | O_CREAT | O_EXCL, 0);
close(fd);
}
int main(void) {
int fd = open(PID_FILE, O_RDONLY);
if (fd > 0) {
close(fd);
return 0;
}
// make sure only one instance is running
create_pidfile();
}
Just run this code on a seperate thread:
void lock() {
while(1) {
ofstream closer("myapplock.locker", ios::trunc);
closer << "locked";
closer.close();
}
}
Run this as your main code:
int main() {
ifstream reader("myapplock.locker");
string s;
reader >> s;
if (s != "locked") {
//your code
}
return 0;
}
Is there a way to detect IP address changes on the local machine in Linux programmatically using C++?
here you go.. this does it without polling.
it only listens for RTM_NEWADDR but it should be easy to change to support RTM_DELADDR if you need
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <net/if.h>
int
main()
{
struct sockaddr_nl addr;
int sock, len;
char buffer[4096];
struct nlmsghdr *nlh;
if ((sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) {
perror("couldn't open NETLINK_ROUTE socket");
return 1;
}
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_groups = RTMGRP_IPV4_IFADDR;
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
perror("couldn't bind");
return 1;
}
nlh = (struct nlmsghdr *)buffer;
while ((len = recv(sock, nlh, 4096, 0)) > 0) {
while ((NLMSG_OK(nlh, len)) && (nlh->nlmsg_type != NLMSG_DONE)) {
if (nlh->nlmsg_type == RTM_NEWADDR) {
struct ifaddrmsg *ifa = (struct ifaddrmsg *) NLMSG_DATA(nlh);
struct rtattr *rth = IFA_RTA(ifa);
int rtl = IFA_PAYLOAD(nlh);
while (rtl && RTA_OK(rth, rtl)) {
if (rth->rta_type == IFA_LOCAL) {
char name[IFNAMSIZ];
if_indextoname(ifa->ifa_index, name);
char ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, RTA_DATA(rth), ip, sizeof(ip));
printf("interface %s ip: %s\n", name, ip);
}
rth = RTA_NEXT(rth, rtl);
}
}
nlh = NLMSG_NEXT(nlh, len);
}
}
return 0;
}
In C, to get the current IP I use:
int s;
struct ifreq ifr = {};
s = socket(PF_INET, SOCK_DGRAM, 0);
strncpy(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name));
if (ioctl(s, SIOCGIFADDR, &ifr) >= 0)
printf("%s\n",
inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr));
Replace "eth0" with the interface you're looking at. All you now need to do is poll for a change.
It is not easy in any way. Each linux distribution uses different places to store IP addresses, etc. (more variation if you consider other UNIX variants). You can use, for example, /sbin/ifconfig to obtain the IP addresses of the interfaces, but you cannot even be sure if you'll find it at this place, or at all, etc.
Also, given you have that executable, you have to set up a thread calling it to obtain the data with a given period (say 5 seconds), and interpret the output. It may vary, for example, if you have bridges, etc. etc. That is, it is not easy.
A solution that comes to my mind is, if you have the opportunity of using GNOME or some other widespread distribution as KDE, you can rely on the messages/informations they give. For example, NetworkManager outputs a signal to the DBUS standard bus when a device changes. You have to implement a listener for those signal. Information here (not working right now, so here is a cache). Note the different messages when a new interface is added, or when one of them changes the IP address. This is the best way I can think of right now.
If your users use NetworkManager, you can poll NetworkManager.Connection.Active and NetworkManager.IP4Config via D-Bus to get a more cross distribution way of determining this information.
If iproute2 is installed and you're on a 2.6 kernel,
/sbin/ip monitor
Will output changes in local interface status and addresses to stdout. Your program can read this.
You could also use the same low level mechanism as the iproute2 tool does (I think it's a netlink socket).
ste's suggestion to use ioctl SIOCGIFADDR used to be technically correct, unfortunately it is unreliable for modern Linux systems, where a single interface can have multiple addresses without using sub-interfaces (e.g. eth0:1) as was done with the now-obsolete ifconfig.
Your best bet is to use getifaddrs(3), which is present from glibc 2.3: http://www.kernel.org/doc/man-pages/online/pages/man3/getifaddrs.3.html
Unfortunately it's somewhat inefficient (you get back a linked list of all addresses on all interfaces and will have to iterate through to find the ones you're interested in), but in most cases you're probably not checking it more than once a minute or so, making the overhead tolerable.
One way would be to write a cron job which contains a call to one the gethost family of library functions. If you use gethostbyname() you can compare the return values of h_addr_list. See man gethostbyname.
If you're want to do this from within your program, spawn a pthread which does the same thing, then sleeps for some arbitrary period of time.
Complete tested example in C with notifications watched for in separate thread:
#include <sys/socket.h> // AF_INET, socket(), bind()
#include <ifaddrs.h> // struct ifaddrs, getifaddrs()
#include <netinet/in.h> // struct sockaddr_in
#include <arpa/inet.h> // inet_ntoa(), htonl()
#include <net/if.h> // if_indextoname()
#include <pthread.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <stdbool.h>
typedef enum {
IP_ADDR_ADD,
IP_ADDR_REMOVE
} ip_address_change_notification_type_t;
typedef void (*ip_address_change_notification_callback_t)(ip_address_change_notification_type_t type, uint32_t ipaddr, void *userdata);
static int ip_address_change_notification_socket = -1;
static pthread_t ip_address_change_notification_thread;
static ip_address_change_notification_callback_t ip_address_change_notification_callback;
static void *ip_address_change_notification_callback_userdata;
void *ip_address_change_notification_worker(void *arg)
{
fprintf(stderr, "ip_address_change_notification_worker entered.\n");
if (ip_address_change_notification_socket == -1) {
goto done;
}
char buffer[4096];
struct nlmsghdr *nlh = (struct nlmsghdr *)buffer;
int len;
while ((len = recv(ip_address_change_notification_socket, nlh, sizeof(buffer), 0)) > 0) {
for (; (NLMSG_OK(nlh, len)) && (nlh->nlmsg_type != NLMSG_DONE); nlh = NLMSG_NEXT(nlh, len)) {
if (nlh->nlmsg_type == RTM_NEWADDR) {
fprintf(stderr, "Netlink: RTM_NEWADDR\n");
} else if (nlh->nlmsg_type == RTM_DELADDR) {
fprintf(stderr, "Netlink: RTM_DELADDR\n");
} else {
fprintf(stderr, "Netlink: nlmsg_type=%d\n", nlh->nlmsg_type);
continue; // Some other kind of announcement.
}
struct ifaddrmsg *ifa = (struct ifaddrmsg *)NLMSG_DATA(nlh);
struct rtattr *rth = IFA_RTA(ifa);
int rtl = IFA_PAYLOAD(nlh);
for (; rtl && RTA_OK(rth, rtl); rth = RTA_NEXT(rth,rtl)) {
char name[IFNAMSIZ];
uint32_t ipaddr;
if (rth->rta_type != IFA_LOCAL) continue;
ipaddr = *((uint32_t *)RTA_DATA(rth)); // In network byte-order.
fprintf(stderr, "Interface %s %s has IP address %s\n", if_indextoname(ifa->ifa_index, name), (nlh->nlmsg_type == RTM_NEWADDR ? "now" : "no longer"), inet_ntoa(*((struct in_addr *)&ipaddr)));
if (ip_address_change_notification_callback) (*ip_address_change_notification_callback)((nlh->nlmsg_type == RTM_NEWADDR ? IP_ADDR_ADD : IP_ADDR_REMOVE), ipaddr, ip_address_change_notification_callback_userdata);
}
}
}
done:
fprintf(stderr, "ip_address_change_notification_worker exited.\n");
return (NULL);
}
bool begin_ip_address_change_notifications(ip_address_change_notification_callback_t callback, void *userdata)
{
if (ip_address_change_notification_socket != -1) return false;
ip_address_change_notification_callback = callback;
ip_address_change_notification_callback_userdata = userdata;
if ((ip_address_change_notification_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) {
perror("begin_ip_address_change_notifications socket");
return false;
}
struct sockaddr_nl addr;
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_groups = RTMGRP_IPV4_IFADDR;
if (bind(ip_address_change_notification_socket, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
perror("begin_ip_address_change_notifications bind");
goto bail;
}
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, 1); // Preclude the need to do pthread_join on the thread after it exits.
int err = pthread_create(&ip_address_change_notification_thread, &attr, ip_address_change_notification_worker, NULL);
pthread_attr_destroy(&attr);
if (err != 0) {
fprintf(stderr, "Error creating ip address change notification thread.\n");
goto bail;
}
return (true);
bail:
close(ip_address_change_notification_socket);
ip_address_change_notification_socket = -1;
ip_address_change_notification_callback = NULL;
ip_address_change_notification_callback_userdata = NULL;
return false;
}
void end_ip_address_change_notifications(void)
{
if (ip_address_change_notification_socket == -1) return;
pthread_cancel(ip_address_change_notification_thread);
close(ip_address_change_notification_socket);
ip_address_change_notification_socket = -1;
ip_address_change_notification_callback = NULL;
ip_address_change_notification_callback_userdata = NULL;
}
From man page of rtnetlink:
DESCRIPTION
Rtnetlink allows the kernel's routing tables to be read and altered. It is used within the kernel to communicate between various subsystems, though this usage is not documented here, and for communication with user-space programs. Network routes, ip addresses, link parameters, neighbor setups, queueing disciplines, traffic classes and packet classifiers may all be controlled through NETLINK_ROUTE sockets. It is based on netlink messages, see netlink(7) for more information.
Using the libnl-3 library, detect link and ip4 address change.
Reference - https://www.infradead.org/~tgr/libnl/doc/core.html#_introduction
#include <netlink/netlink.h>
#include <netlink/socket.h>
#include <netlink/msg.h>
#include <arpa/inet.h>
#include <iostream>
static char ip4Addr[INET_ADDRSTRLEN];
static int parseAddress(struct nlmsghdr *hdr)
{
std::cout << "parseAddress" << std::endl;
struct ifaddrmsg *iface = (struct ifaddrmsg *)nlmsg_data(hdr);
struct nlattr *attrs[IFA_MAX + 1];
if (nlmsg_parse(hdr, sizeof(struct ifaddrmsg), attrs, IFA_MAX, nullptr) < 0)
{
std::cerr << "problem parsing Netlink response" << std::endl;
return -1;
}
if (attrs[IFA_ADDRESS] == nullptr)
{
std::cerr << "Address Never Received "
<< std::endl;
return -1;
}
inet_ntop(iface->ifa_family, nla_data(attrs[IFA_ADDRESS]), ip4Addr, sizeof(ip4Addr));
if ((hdr->nlmsg_type == RTM_NEWADDR) && (iface->ifa_family == AF_INET))
{
std::cout << "IPv4 Address added : " << ip4Addr << std::endl;
}
if ((hdr->nlmsg_type == RTM_DELADDR) && (iface->ifa_family == AF_INET))
{
std::cout << "IPv4 Address deleted : " << ip4Addr << std::endl;
}
return 0;
}
static int parseLink(struct nlmsghdr *hdr)
{
std::cout << "parseLink" << std::endl;
struct ifinfomsg *iface = (struct ifinfomsg *)nlmsg_data(hdr);
struct nlattr *attrs[IFLA_MAX + 1];
if (nlmsg_parse(hdr, sizeof(struct ifinfomsg), attrs, IFLA_MAX, nullptr) < 0)
{
std::cerr << "problem parsing Netlink response" << std::endl;
return -1;
}
if (attrs[IFLA_IFNAME] != nullptr)
{
if (hdr->nlmsg_type == RTM_NEWLINK)
{
std::cout << (char *)nla_data(attrs[IFLA_IFNAME]) << std::endl;
}
else if (hdr->nlmsg_type == RTM_DELLINK)
{
std::cout << (char *)nla_data(attrs[IFLA_IFNAME]) << std::endl;
}
}
return 0;
}
static int receiveNewMsg(struct nl_msg *msg, void *arg)
{
struct nlmsghdr *nlh = nlmsg_hdr(msg);
int len = nlh->nlmsg_len;
int type = nlh->nlmsg_type;
while (nlmsg_ok(nlh, len))
{
if (type != RTM_NEWLINK && type != RTM_DELLINK && type != RTM_NEWADDR && type != RTM_DELADDR)
{
if (nlh->nlmsg_type == NLMSG_DONE)
{
std::cout << "message complete" << std::endl;
}
nlh = nlmsg_next(nlh, &len);
continue;
}
if ((nlh->nlmsg_type == RTM_NEWLINK) || (nlh->nlmsg_type == RTM_DELLINK))
{
parseLink(nlh);
}
if ((nlh->nlmsg_type == RTM_NEWADDR) || (nlh->nlmsg_type == RTM_DELADDR))
{
parseAddress(nlh);
}
nlh = nlmsg_next(nlh, &len);
}
return 1;
}
int main(int argc, char const *argv[])
{
struct nl_sock *sk;
/* Allocate a new socket */
sk = nl_socket_alloc();
/*
* Notifications do not use sequence numbers, disable sequence number checking.
*/
nl_socket_disable_seq_check(sk);
/*
* Define a callback function, which will be called for each notification received
*/
nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, receiveNewMsg, nullptr);
nl_socket_modify_cb(sk, NL_CB_FINISH, NL_CB_CUSTOM, receiveNewMsg, nullptr);
/* Connect to routing netlink protocol */
nl_connect(sk, NETLINK_ROUTE);
/* Subscribe to link notifications group */
nl_socket_add_memberships(sk, RTNLGRP_LINK, 0);
nl_socket_add_memberships(sk, RTNLGRP_IPV4_IFADDR, 0);
/*
* Start receiving messages. The function nl_recvmsgs_default() will block
* until one or more netlink messages (notification) are received which
* will be passed on to my_func().
*/
while (1)
nl_recvmsgs_default(sk);
return 0;
}