So I'm trying to code a multi-threading server. I've spent an enormous time on the internet figuring out the correct way to do this and the answer as always seems to be it depends. Whenever I execute my code, the client successfully connects, and executes but when the thread terminates and returns to the while loop the whole program segfaults.
I probably could use a good spanking on a few other things as well such as my usage of global variables. The entirety of code is below, sorry for the inconsistent space/tabbing.
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <signal.h>
#include <math.h>
#include <pthread.h>
#include <sys/stat.h>
#include <fcntl.h>
/* ---------------------------------------------------------------------
This is a basic whiteboard server. You can query it, append to it and
clear in it. It understands both encrypted and unencrypted data.
--------------------------------------------------------------------- */
struct whiteboard {
int line;
char type;
int bytes;
char string[1024];
} *Server;
int serverSize, threadcount, id[5];
bool debug = true;
struct whiteboard *Server;
pthread_mutex_t mutex;
pthread_t thread[5];
/* -------------------------------------------
function: sigint_handler
Opens a file "whiteboard.all" in writemode
and writes all white board information in
command mode.
------------------------------------------- */
void sigint_handler(int sig)
{
if (debug) printf("\nInduced SIGINT.\n");
FILE *fp;
fp=fopen("whiteboard.all","w");
int x=0;
for (x;x<serverSize;x++) // Loop Responsible for iterating all the whiteboard entries.
{
if (debug) printf("#%d%c%d\n%s\n",Server[x].line,Server[x].type,Server[x].bytes,Server[x].string);
fprintf(fp,"#%d%c%d\n%s\n",Server[x].line,Server[x].type,Server[x].bytes,Server[x].string);
}
if (debug) printf("All values stored.\n");
free(Server); // Free dynamically allocated memory
exit(1);
}
/* -------------------------------------------
function: processMessage
Parses '!' messages into their parts -
returns struct in response.
------------------------------------------- */
struct whiteboard processMessage(char * message)
{
int lineNumber, numBytes;
char stringType, entry[1028];
if (debug) printf("Update Statement!\n");
// Read line sent by Socket
sscanf(message,"%*c%d%c%d\n%[^\n]s",&lineNumber,&stringType,&numBytes,entry);
if (debug) printf("Processed: Line: %d, Text: %s\n",lineNumber,entry);
// Parse information into local Struct
struct whiteboard Server;
Server.line = lineNumber;
Server.type = stringType;
Server.bytes = numBytes;
strcpy(Server.string,entry);
// If there is no bytes, give nothing
if (numBytes == 0)
{
strcpy(Server.string,"");
}
return Server;
}
/* -------------------------------------------
function: handleEverything
Determines type of message recieved and
process and parses accordingly.
------------------------------------------- */
char * handleEverything(char* message, struct whiteboard *Server, char* newMessage)
{
bool updateFlag = false, queryFlag = false;
// If message is an Entry
if (message[0] == '#')
{
if (debug) printf("Triggered Entry!\n");
// Create Temporary Struct
struct whiteboard messageReturn;
messageReturn = processMessage(message);
// Store Temporary Struct in Correct Heap Struct
Server[messageReturn.line] = messageReturn;
sprintf(newMessage,"!%d%c%d\n%s\n",messageReturn.line, messageReturn.type, messageReturn.bytes, messageReturn.string);
return newMessage;
}
// If message is a query
if (message[0] == '?')
{
if (debug) printf("Triggered Query!\n");
int x;
queryFlag = true;
sscanf(message,"%*c%d",&x); // Parse Query
if (x > serverSize) // Check if Query out of Range
{
strcpy(newMessage,"ERROR: Query out of Range.\n");
return newMessage;
}
sprintf(newMessage,"!%d%c%d\n%s\n",Server[x].line,Server[x].type,Server[x].bytes,Server[x].string);
if (debug) printf("newMessage as of handleEverything:%s\n",newMessage);
return newMessage;
}
}
/* -------------------------------------------
function: readFile
If argument -f given, read file
process and parse into heap memory.
------------------------------------------- */
void readFile(char * filename)
{
FILE *fp;
fp=fopen(filename,"r");
int line, bytes, count = 0, totalSize = 0;
char type, check, string[1028], individualLine[1028];
// Loop to determine size of file. **I know this is sloppy.
while (fgets(individualLine, sizeof(individualLine), fp))
{
totalSize++;
}
// Each line shoud have totalSize - 2 (to account for 0)
// (answer) / 2 to account for string line and instruction.
totalSize = (totalSize - 2) / 2;
serverSize = totalSize+1;
if (debug) printf("Total Size is: %d\n",serverSize);
// Open and Allocate Memory
fp=fopen(filename,"r");
if (debug) printf("File Mode Calloc Initialize\n");
Server = calloc(serverSize+2, sizeof(*Server));
// Write to Heap Loop
while (fgets(individualLine, sizeof(individualLine), fp)) {
if (individualLine[0] == '#') // Case of Header Line
{
sscanf(individualLine,"%c%d%c%d",&check,&line,&type,&bytes);
if (debug) printf("Count: %d, Check:%c, Line:%d, Type: %c, Bytes:%d \n",count,check,line,type,bytes);
Server[count].line = line;
Server[count].type = type;
Server[count].bytes = bytes;
count++;
}
else
{
// For case of no data
if (individualLine[0] == '\n')
{
strcpy(string,"");
}
// Then scan data line
sscanf(individualLine,"%[^\n]s",string);
if (debug) printf("String: %s\n",string);
strcpy(Server[count-1].string,string);
}
}
return;
}
void *threadFunction(int snew)
{
char tempmessage[1024], message[2048];
// Compile and Send Server Message
strcpy(tempmessage, "CMPUT379 Whiteboard Server v0\n");
send(snew, tempmessage, sizeof(tempmessage), 0);
// Recieve Message
char n = recv(snew, message, sizeof(message), 0);
pthread_mutex_lock(&mutex);
if (debug) printf("Attempt to Malloc for newMessage\n");
char * newMessage = malloc(1024 * sizeof(char));
if (debug) printf("goto: handleEverything\n");
newMessage = handleEverything(message, Server, newMessage);
if (debug) printf("returnMessage:%s\n",newMessage);
strcpy(message,newMessage);
free(newMessage);
pthread_mutex_unlock(&mutex);
if (debug) printf("message = %s\n", message);
send(snew, message, sizeof(message), 0);
printf("End of threadFunction\n");
return;
}
/* -------------------------------------------
function: main
Function Body of Server
------------------------------------------- */
int main(int argc, char * argv[])
{
int sock, fromlength, outnum, i, socketNumber, snew;
bool cleanMode;
// Initialize Signal Handling
struct sigaction act;
act.sa_handler = sigint_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);
// For correct number of arguments.
if (argc == 4)
{
// If "-n" parameter (cleanMode)
if (strcmp(argv[2], "-n") == 0)
{
// Get size + 1
cleanMode = true;
sscanf(argv[3],"%d",&serverSize);
serverSize += 1;
if (debug) printf("== Clean Mode Properly Initiated == \n");
if (debug) printf("serverSize: %d\n",serverSize);
if (debug) printf("Clean Mode Calloc\n");
Server = calloc(serverSize, sizeof(*Server));
int i = 0;
for (i; i < serverSize; i++) // Initialize allocated Memory
{
Server[i].line = i;
Server[i].type = 'p';
Server[i].bytes = 0;
strcpy(Server[i].string,"");
}
}
// If "-f" parameter (filemode)
else if (strcmp(argv[2], "-f") == 0)
{
// Read File
cleanMode = false;
readFile(argv[3]);
if (debug) printf("== Statefile Mode Properly Initiated == \n");
if (debug) printf("serverSize: %d\n",serverSize);
}
// Otherwise incorrect parameter.
else
{
printf("Incorrect Argument. \n");
printf("Usage: wbs279 pornumber {-n number | -f statefile}\n");
exit(1);
}
sscanf(argv[1],"%d",&socketNumber);
}
// Send Error for Incorrect Number of Arguments
if (argc != 4)
{
printf("Error: Incorrect Number of Input Arguments.\n");
printf("Usage: wbs279 portnumber {-n number | -f statefile}\n");
exit(1);
}
// == Do socket stuff ==
char tempmessage[1024], message[2048];
struct sockaddr_in master, from;
if (debug) printf("Assrt Socket\n");
sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
perror ("Server: cannot open master socket");
exit (1);
}
master.sin_family = AF_INET;
master.sin_addr.s_addr = INADDR_ANY;
master.sin_port = htons (socketNumber);
if (bind (sock, (struct sockaddr*) &master, sizeof (master)))
{
perror ("Server: cannot bind master socket");
exit (1);
}
// == Done socket stuff ==
listen (sock, 5);
int threadNumber = 0;
while(1)
{
printf("But what about now.\n");
if (debug) printf("-- Wait for Input --\n");
printf("Enie, ");
fromlength = sizeof (from);
printf("Meanie, ");
snew = accept (sock, (struct sockaddr*) & from, & fromlength);
printf("Miney, ");
if (snew < 0)
{
perror ("Server: accept failed");
exit (1);
}
printf("Moe\n");
pthread_create(&thread[threadNumber],NULL,threadFunction(snew), &id[threadNumber]);
//printf("Can I join?!\n");
//pthread_join(thread[0],NULL);
//printf("Joined?!\n");
threadNumber++;
close (snew);
}
}
I'm also curious as to how exactly to let multiple clients use the server at once. Is how I've allocated the whiteboard structure data appropriate for this process?
I'm very sorry if these don't make any sense.
You seem to somehow expect this:
pthread_create(&thread[threadNumber],NULL,threadFunction(snew), &id[threadNumber]);
/* ... */
close (snew);
To make sense, while it clearly doesn't.
Instead of starting a thread that runs threadFunction, passing it snew, you call the thread function and pass the return value to pthread_create(), which will interpret it as a function pointer. This will break, especially considering that the thread function incorrectly ends with:
return;
This shouldn't compile, since it's declared to return void *.
Also assuming you managed to start the thread, passing it snew to use as its socket: then you immediately close that socket, causing any reference to it from the thread to be invalid!
Please note that pthread_create() does not block and wait for the thread to exit, that would be kind of ... pointless. It starts off the new thread to run in parallel with the main thread, so of course you can't yank the carpet away from under it.
This signal handler is completely unsafe:
void sigint_handler(int sig)
{
if (debug) printf("\nInduced SIGINT.\n");
FILE *fp;
fp=fopen("whiteboard.all","w");
int x=0;
for (x;x<serverSize;x++) // Loop Responsible for iterating all the whiteboard entries.
{
if (debug) printf("#%d%c%d\n%s\n",Server[x].line,Server[x].type,Server[x].bytes,Server[x].string);
fprintf(fp,"#%d%c%d\n%s\n",Server[x].line,Server[x].type,Server[x].bytes,Server[x].string);
}
if (debug) printf("All values stored.\n");
free(Server); // Free dynamically allocated memory
exit(1);
}
Per 2.4.3 Signal Actions of the POSIX standard (emphasis added):
The following table defines a set of functions that shall be
async-signal-safe. Therefore, applications can call them, without
restriction, from signal-catching functions. ...
[list of async-signal-safe functions]
Any function not in the above table may be unsafe with respect to signals. Implementations may make other interfaces
async-signal-safe. In the presence of signals, all functions defined
by this volume of POSIX.1-2008 shall behave as defined when called
from or interrupted by a signal-catching function, with the exception
that when a signal interrupts an unsafe function or equivalent
(such as the processing equivalent to exit() performed after a return
from the initial call to main()) and the signal-catching function
calls an unsafe function, the behavior is undefined. Additional
exceptions are specified in the descriptions of individual functions
such as longjmp().
Your signal handler invokes undefined behavior.
Related
So I am learning about buffer overflow attacks in C. I understand what they are and I can find a buffer overflow vulnerability in a simple C code. Simple is fine :).
But this code seems to go beyond my definition of 'simple'.
So far, I understand that in this C code, buffer overflow vulnerabilities can happen mainly in the line: strcpy(retstr, "Process Error."); but there is an if statement above the line that I think protects against buffer overflow at this line.
I would appreciate any help in finding the buffer overflow vulnerability in this code.
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <time.h>
#define CANBUFSIZE 106
#define MSGBUFSIZE 256
#define TIMEBUFSIZE 128
char msgbuf[MSGBUFSIZE];
char canarybuf[CANBUFSIZE];
void get_time(char* format, char* retstr, unsigned received)
{
// memory for our local copy of the timestring
char timebuf[TIMEBUFSIZE];
time_t curtime;
// if the format string esceeds our local buffer ...
if(strlen(format) > TIMEBUFSIZE)
{
strcpy(retstr,"Process Error.");
return;
}
// otherwise create a local working copy
memcpy(timebuf,format,received);
// Get the current time.
curtime = time (NULL);
// Convert it to local time representation.
// and convert the format string to the real timestring
struct tm *loctime = localtime (&curtime);
strftime(retstr,TIMEBUFSIZE,timebuf,loctime);
return;
}
int main(int argc, char** argv)
{
int port; // the portnumber of our service
struct in_addr bind_addr; // bind address of the server
int sd; // the socketdescriptor
struct sockaddr_in addr; // address of our service
struct sockaddr_in addr_from; //address of the client
int addrlen = sizeof(addr_from);
int pid; // our process id
int sid; // our session id
unsigned received; // number of bytes received from network
// resolve command line arguments
if(argc != 3)
{
printf("Usage: timeservice <bind address> <portnum>\n");
return 1;
}
if (inet_aton(argv[1], &bind_addr) == 0)
{
fprintf(stderr, "Invalid bind address\n");
exit(EXIT_FAILURE);
}
port = atoi(argv[2]);
if ((port < 1024) || (port > 65535))
{
printf("Portrange has to be between 1024 and 65535.\n");
exit(EXIT_FAILURE);
}
// forking to background
pid = fork();
if(pid < 0)
{
printf("fork() failed\n");
exit(EXIT_FAILURE);
}
// we are parent
else if(pid > 0)
{
return 0;
}
/*
* we are the child process
* because of the termination of our parent, we need a new session id,
* else we are zombie
*/
sid = setsid();
if (sid < 0) {
return 1;
}
/*
* since we are a system service we have to close all standard file
* descriptors
*/
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
// create an udp socket
if((sd = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP)) < 0)
{
return 1;
}
// clear the memory of our addr struct
memset(&addr,0,sizeof(addr));
// Protocol Family = IPv4
addr.sin_family = PF_INET;
// Listen on bindAddr and bindPort only
addr.sin_addr.s_addr = bind_addr.s_addr;
addr.sin_port = htons(port);
// bind to the udp socket
if(bind(sd,(struct sockaddr*)&addr,sizeof(addr)) != 0)
{
return 1;
}
for(;;)
{
// prepare memory
memset(&msgbuf, 0, sizeof(msgbuf));
received = recvfrom(sd,msgbuf,MSGBUFSIZE,MSG_WAITALL,
(struct sockaddr*)&addr_from,(socklen_t*) &addrlen);
// fork a new child
pid = fork();
// we are parent
if (pid > 0)
{
// wait for the child to finish
waitpid(pid,NULL,0);
}
else
{
/*
* we are inside the child process
*/
// reserve some memory for our response
char * returnstr = (char*) malloc(TIMEBUFSIZE);
// analyse the client request and format the time string
get_time(msgbuf, returnstr, received);
// send our response to the client
sendto(sd,returnstr,strlen(returnstr)+1,MSG_DONTWAIT,
(struct sockaddr *) &addr_from, addrlen);
free(returnstr);
return EXIT_SUCCESS;
}
}
close(sd);
return 0;
}
There is a discrepancy in get_time: strlen is used to check the "size" of the incoming buffer, but memcpy is used with a user-supplied received argument. It suffices to pass a buffer with a NUL byte within the first TIMEBUFSIZE bytes.
You can trigger the crash directly in code if you do:
received = 256;
memset(msgbuf, 'A', MSGBUFSIZE);
msgbuf[0] = 0;
this will "fill up" msgbuf with 256 bytes and then keep writing for 128 bytes more, overwriting the return address on the stack to an address of your choice. Because the first byte is a NUL, the strlen check passes.
If you want to trigger this on the actual binary, you probably need something like: (assuming it runs on localhost:1234)
perl -MIO::Socket::IP -E '
$buf = "\0" . ("A"x255);
my $s = IO::Socket::IP->new(PeerHost => "127.0.0.1", PeerPort => 1234, Type => SOCK_DGRAM);
$s->autoflush(1);
print $s $buf;
'
and then of course you need to modify the buffer to perform actual code flow
I am writing a small http proxy server(in C) on a linux machine, Ubuntu 18.04.1 to be specific, and I've been trying to find a way to get the pid of the process that is connecting to it.
It might be of use to mention that the proxy is intended to proxy connections only for processes running on the same machine, so I guess this should make this task possible.
The server uses AF_INET family sockets along with read/write operations in order to do it's job; I am mentioning this because after some research I did encounter threads about "ancillary data",for example: Is there a way to get the uid of the other end of a unix socket connection
Ancillary data contain credentials of the connecting socket(such as PID), but only work on AF_UNIX sockets, used for local IPC, and requires us to explicitly send/receive it on both sides(client/server). In my case, although, as I mentioned, the server will only proxy traffic on the same machine as the server, I need to use AF_INET sockets, so everyone(e.g. web browser) is able to connect to it.
Performance is not so critical; so any suggestions(including workarounds using system calls etc.) are very welcome.
We can use netstat -nptW output to see which local processes' TCP connections. As the output may be security sensitive, superuser privileges are required to see processes belonging to all users.
Since there is no reason to run a proxy service with elevated privileges (expect perhaps CAP_NET_BIND_SERVICE), a privileged helper program is needed.
I pondered a suitable security model for a bit, and came to the conclusion that a helper which examines the connected socket given to it (as say standard input), and outputs just the peer PID(s), would be safest: it would be extremely hard to misuse it, and even if possible, only the peer process ID is revealed.
Here is the example helper, tcp-peer-pids.c:
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#define EXITCODE_OK 0
#define EXITCODE_STDIN_INVALID 1
#define EXITCODE_UNKNOWN_ADDRESS 2
#define EXITCODE_NETSTAT 3
#define EXITCODE_NETSTAT_OUTPUT 4
#define EXITCODE_WRITE_ERROR 5
#define EXITCODE_PRIVILEGES 6
static pid_t *pids = NULL;
static size_t num_pids = 0;
static size_t max_pids = 0;
static int add_pid(const pid_t p)
{
size_t i;
/* Check if already listed. */
for (i = 0; i < num_pids; i++)
if (pids[i] == p)
return 0;
/* Ensure enough room in pids array. */
if (num_pids >= max_pids) {
const size_t max_temp = (num_pids | 1023) + 1025 - 8;
pid_t *temp;
temp = realloc(pids, max_temp * sizeof pids[0]);
if (!temp)
return ENOMEM;
pids = temp;
max_pids = max_temp;
}
pids[num_pids++] = p;
return 0;
}
int main(void)
{
struct sockaddr_storage sock_addr;
socklen_t sock_addrlen = sizeof sock_addr;
char sock_match[128], sock_host[64], sock_port[32];
struct sockaddr_storage peer_addr;
socklen_t peer_addrlen = sizeof peer_addr;
char peer_match[128], peer_host[64], peer_port[32];
FILE *cmd;
char *line = NULL;
size_t size = 0;
ssize_t len;
int status;
/* Socket address is *remote*, and peer address is *local*.
This is because the variables are named after their matching netstat lines. */
if (getsockname(STDIN_FILENO, (struct sockaddr *)&sock_addr, &sock_addrlen) == -1) {
fprintf(stderr, "Standard input is not a valid socket.\n");
exit(EXITCODE_STDIN_INVALID);
}
if (getpeername(STDIN_FILENO, (struct sockaddr *)&peer_addr, &peer_addrlen) == -1) {
fprintf(stderr, "Standard input is not a connected socket.\n");
exit(EXITCODE_STDIN_INVALID);
}
if ((sock_addr.ss_family != AF_INET && sock_addr.ss_family != AF_INET6) ||
(peer_addr.ss_family != AF_INET && peer_addr.ss_family != AF_INET6)) {
fprintf(stderr, "Standard input is not an IP socket.\n");
exit(EXITCODE_STDIN_INVALID);
}
/* For security, we close the standard input descriptor, */
close(STDIN_FILENO);
/* and redirect it from /dev/null, if possible. */
{
int fd = open("/dev/null", O_RDONLY);
if (fd != -1 && fd != STDIN_FILENO) {
dup2(fd, STDIN_FILENO);
close(fd);
}
}
/* Convert sockets to numerical host and port strings. */
if (getnameinfo((const struct sockaddr *)&sock_addr, sock_addrlen,
sock_host, sizeof sock_host, sock_port, sizeof sock_port,
NI_NUMERICHOST | NI_NUMERICSERV)) {
fprintf(stderr, "Unknown socket address.\n");
exit(EXITCODE_UNKNOWN_ADDRESS);
}
if (getnameinfo((const struct sockaddr *)&peer_addr, peer_addrlen,
peer_host, sizeof peer_host, peer_port, sizeof peer_port,
NI_NUMERICHOST | NI_NUMERICSERV)) {
fprintf(stderr, "Unknown peer address.\n");
exit(EXITCODE_UNKNOWN_ADDRESS);
}
/* Combine to the host:port format netstat uses. */
snprintf(sock_match, sizeof sock_match, "%s:%s", sock_host, sock_port);
snprintf(peer_match, sizeof peer_match, "%s:%s", peer_host, peer_port);
/* Switch to privileged user, if installed as setuid. */
{
uid_t real_uid = getuid();
gid_t real_gid = getgid();
uid_t effective_uid = geteuid();
gid_t effective_gid = getegid();
if (real_gid != effective_gid || real_uid != effective_uid) {
/* SetUID or SetGID in effect. Switch privileges. */
if (setresgid(effective_gid, effective_gid, effective_gid) == -1 ||
setresuid(effective_uid, effective_uid, effective_uid) == -1) {
fprintf(stderr, "Error in privileges: %s.\n", strerror(errno));
exit(EXITCODE_PRIVILEGES);
}
}
}
/* Run netstat to obtain the data; redirect standard error to standard output. */
cmd = popen("LANG=C LC_ALL=C /bin/netstat -nptW 2>&1", "r");
if (!cmd) {
fprintf(stderr, "Cannot run netstat.\n");
exit(EXITCODE_NETSTAT);
}
/* Input line loop. */
while (1) {
char *field[8], *ends;
long val;
pid_t p;
len = getline(&line, &size, cmd);
if (len < 1)
break;
/* Split each line into fields. */
field[0] = strtok(line, "\t\n\v\f\r "); /* Protocol */
/* We are only interested in tcp ("tcp" and "tcp6" protocols). */
if (strcmp(field[0], "tcp") && strcmp(field[0], "tcp6"))
continue;
field[1] = strtok(NULL, "\t\n\v\f\r "); /* Recv-Q */
field[2] = strtok(NULL, "\t\n\v\f\r "); /* Send-Q */
field[3] = strtok(NULL, "\t\n\v\f\r "); /* Local address (peer) */
field[4] = strtok(NULL, "\t\n\v\f\r "); /* Remote address (sock) */
field[5] = strtok(NULL, "\t\n\v\f\r "); /* State */
field[6] = strtok(NULL, "\t\n\v\f\r /"); /* PID */
field[7] = strtok(NULL, "\t\n\v\f\r "); /* Process name */
/* Local address must match peer_match, and foreign/remote sock_match. */
if (strcmp(field[3], peer_match) || strcmp(field[4], sock_match))
continue;
/* This line corresponds to the process we are looking for. */
/* Missing PID field is an error at this point. */
if (!field[6])
break;
/* Parse the PID. Parsing errors are fatal. */
ends = field[6];
errno = 0;
val = strtol(field[6], &ends, 10);
if (errno || ends == field[6] || *ends != '\0' || val < 1)
break;
p = (pid_t)val;
if ((long)p != val)
break;
/* Add the pid to the known pids list. */
if (add_pid(p))
break;
}
/* The line buffer is no longer needed. */
free(line);
/* I/O error? */
if (!feof(cmd) || ferror(cmd)) {
fprintf(stderr, "Error reading netstat output.\n");
exit(EXITCODE_NETSTAT_OUTPUT);
}
/* Reap the netstat process. */
status = pclose(cmd);
if (status == -1) {
fprintf(stderr, "Error reading netstat output: %s.\n", strerror(errno));
exit(EXITCODE_NETSTAT_OUTPUT);
}
if (!WIFEXITED(status)) {
fprintf(stderr, "Netstat died unexpectedly.\n");
exit(EXITCODE_NETSTAT_OUTPUT);
}
if (WEXITSTATUS(status)) {
fprintf(stderr, "Netstat failed with exit status %d.\n", WEXITSTATUS(status));
exit(EXITCODE_NETSTAT_OUTPUT);
}
/* Output the array of pids as binary data. */
if (num_pids > 0) {
const char *head = (const char *)pids;
const char *const ends = (const char *)(pids + num_pids);
ssize_t n;
while (head < ends) {
n = write(STDOUT_FILENO, head, (size_t)(ends - head));
if (n > 0)
head += n;
else
if (n != -1)
exit(EXITCODE_WRITE_ERROR);
else
if (errno != EINTR)
exit(EXITCODE_WRITE_ERROR);
}
}
/* Discard the pids array. */
free(pids);
exit(EXITCODE_OK);
}
It can be run using ordinary user privileges (in which case it'll only know about processes owned by that user), root privileges, or as setuid root.
If used with sudo, ensure you use rule proxyuser ALL = NOPASSWD: /path/to/helper, because sudo has no way of asking a password there. I would probably just install the helper as setuid root at /usr/lib/yourproxy/tcp-peer-pid, owner root, group your proxy service group, and no access to other users (root:proxygroup -r-sr-x---).
The helper is tightly coupled to netstat -nptW output format, but does explicitly set the C locale to avoid getting localized output.
The comparison address:port strings to match to "Local Address" and "Foreign Address" in netstat output are constructed from the addresses returned by getpeername() and getsockname(), respectively, using [getnameinfo()(http://man7.org/linux/man-pages/man3/getnameinfo.3.html) in numerical form (using NI_NUMERICHOST | NI_NUMERICSERV flags).
The helper provides the PIDs in binary form to the server, because the server code would have been too long to fit in a single post here otherwise.
Here is an example TCP service, server.c, which uses the above helper to find out the PID of the peer end of the socket on the local computer. (To avoid denial-of-service attacks, you should set an IP filter that rejects accesses to your proxy service port from outside the computer.)
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#ifndef HELPER_PATH
#define HELPER_PATH "./tcp-peer-pids"
#endif
#ifndef HELPER_NAME
#define HELPER_NAME "tcp-peer-pids"
#endif
#ifndef SUDO_PATH
#define SUDO_PATH "/usr/bin/sudo"
#endif
#ifndef SUDO_NAME
#define SUDO_NAME "sudo"
#endif
/*
* Signal handler, to detect INT (Ctrl+C), HUP, and TERM signals.
*/
static volatile sig_atomic_t done = 0;
static void handle_done(int signum)
{
/* In Linux, all signals have signum > 0. */
__atomic_store_n(&done, (sig_atomic_t)signum, __ATOMIC_SEQ_CST);
}
static int install_done(int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_flags = SA_RESTART; /* Do not interrupt slow syscalls. */
act.sa_handler = handle_done;
if (sigaction(signum, &act, NULL) == -1)
return -1; /* errno set by getpeername() */
return 0;
}
/* Helper function: Move descriptors away from STDIN/STDOUT/STDERR.
Returns 0 if successful, -1 with errno set if an error occurs. */
static inline int normalfds(int fd[], const size_t n)
{
unsigned int closemask = 0;
int err = 0;
size_t i;
int newfd;
for (i = 0; i < n; i++)
while (fd[i] == STDIN_FILENO || fd[i] == STDOUT_FILENO || fd[i] == STDERR_FILENO) {
newfd = dup(fd[i]);
if (newfd == -1) {
err = errno;
break;
}
closemask |= 1u << fd[i];
fd[i] = newfd;
}
/* Close temporary descriptors. */
if (closemask & (1u << STDIN_FILENO)) close(STDIN_FILENO);
if (closemask & (1u << STDOUT_FILENO)) close(STDOUT_FILENO);
if (closemask & (1u << STDERR_FILENO)) close(STDERR_FILENO);
/* Success? */
if (!err)
return 0;
/* Report error. */
errno = err;
return -1;
}
/* Return the number of peer processes.
If an error occurs, returns zero; examine errno. */
size_t peer_pids(const int connfd, pid_t *const pids, size_t maxpids)
{
char *in_data = NULL;
size_t in_size = 0;
size_t in_used = 0;
size_t n;
int binpipe[2], status;
pid_t child, p;
/* Sanity check. */
if (connfd == -1) {
errno = EBADF;
return 0;
}
/* Create a pipe to transfer the PIDs (in binary). */
if (pipe(binpipe) == -1)
return 0; /* errno set by pipe(). */
/* Make sure the binary pipe descriptors do not conflict with standard descriptors. */
if (normalfds(binpipe, 2) == -1) {
const int saved_errno = errno;
close(binpipe[0]);
close(binpipe[1]);
errno = saved_errno;
return 0;
}
/* Fork a child process. */
child = fork();
if (child == -1) {
const int saved_errno = errno;
close(binpipe[0]);
close(binpipe[1]);
errno = saved_errno;
return 0;
}
if (!child) {
/* This is the child process. */
#ifdef USE_SUDO
const char *cmd_path = SUDO_PATH;
char *const cmd_args[3] = { SUDO_NAME, HELPER_PATH, NULL };
#else
const char *cmd_path = HELPER_PATH;
char *const cmd_args[2] = { HELPER_NAME, NULL };
#endif
/* The child runs in its own process group, for easier management. */
setsid();
/* Close read end of pipe. */
close(binpipe[0]);
/* Move established connection to standard input. */
if (connfd != STDIN_FILENO) {
if (dup2(connfd, STDIN_FILENO) != STDIN_FILENO)
_Exit(99);
close(connfd);
}
/* Move write end of pipe to standard output. */
if (dup2(binpipe[1], STDOUT_FILENO) != STDOUT_FILENO)
_Exit(99);
else
close(binpipe[1]);
/* Execute helper. */
execv(cmd_path, cmd_args);
/* Failed to execute helper. */
_Exit(98);
}
/* Parent process. */
/* Close write end of pipe, so we detect when child exits. */
close(binpipe[1]);
/* Read all output from child. */
status = 0;
while (1) {
ssize_t bytes;
if (in_used >= in_size) {
const size_t size = (in_used | 1023) + 1025 - 8;
char *temp;
temp = realloc(in_data, in_size);
if (!temp) {
status = ENOMEM;
break;
}
in_data = temp;
in_size = size;
}
bytes = read(binpipe[0], in_data + in_used, in_size - in_used);
if (bytes > 0) {
in_used += bytes;
} else
if (bytes == 0) {
/* End of input condition. */
break;
} else
if (bytes != -1) {
status = EIO;
break;
} else
if (errno != EINTR) {
status = errno;
break;
}
}
/* Close the pipe. */
close(binpipe[0]);
/* Abort, if an error occurred. */
if (status) {
free(in_data);
kill(-child, SIGKILL);
do {
p = waitpid(child, NULL, 0);
} while (p == -1 && errno == EINTR);
errno = status;
return 0;
}
/* Reap the child process. */
do {
status = 0;
p = waitpid(child, &status, 0);
} while (p == -1 && errno == EINTR);
if (p == -1) {
const int saved_errno = errno;
free(in_data);
errno = saved_errno;
return 0;
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
free(in_data);
errno = ESRCH; /* The helper command failed, really. */
return 0;
}
/* We expect an integer number of pid_t's. Check. */
n = in_used / sizeof (pid_t);
if ((in_used % sizeof (pid_t)) != 0) {
free(in_data);
errno = EIO;
return 0;
}
/* None found? */
if (!n) {
free(in_data);
errno = ENOENT; /* Not found, really. */
return 0;
}
/* Be paranoid, and verify the pids look sane. */
{
const pid_t *const pid = (const pid_t *const)in_data;
size_t i;
for (i = 0; i < n; i++)
if (pid[i] < 2) {
free(in_data);
errno = ESRCH; /* Helper failed */
return 0;
}
}
/* Copy to user buffer, if specified. */
if (maxpids > n)
memcpy(pids, in_data, n * sizeof (pid_t));
else
if (maxpids > 0)
memcpy(pids, in_data, maxpids * sizeof (pid_t));
/* The pid buffer is no longer needed. */
free(in_data);
/* Return the number of pids we actually received. */
return n;
}
int main(int argc, char *argv[])
{
struct addrinfo hints, *list, *curr;
const char *node, *serv;
int service_fd, err;
struct sockaddr_storage client_addr;
socklen_t client_addrlen;
int client_fd;
if (argc != 3) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s HOST PORT\n", argv[0]);
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
/* Install signal handers for Ctrl+C, HUP, and TERM. */
if (install_done(SIGINT) ||
install_done(SIGHUP) ||
install_done(SIGTERM)) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* Empty or - or * is a wildcard host. */
if (argv[1][0] == '\0' || !strcmp(argv[1], "-") || !strcmp(argv[1], "*"))
node = NULL;
else
node = argv[1];
serv = argv[2];
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; /* IPv4 or IPv6 */
hints.ai_socktype = SOCK_STREAM; /* TCP */
hints.ai_flags = AI_PASSIVE;
hints.ai_protocol = 0;
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
list = NULL;
err = getaddrinfo(node, serv, &hints, &list);
if (err) {
fprintf(stderr, "Invalid host and/or port: %s.\n", gai_strerror(err));
return EXIT_FAILURE;
}
service_fd = -1;
err = 0;
for (curr = list; curr != NULL; curr = curr->ai_next) {
service_fd = socket(curr->ai_family, curr->ai_socktype, curr->ai_protocol);
if (service_fd == -1)
continue;
errno = 0;
if (bind(service_fd, curr->ai_addr, curr->ai_addrlen) == -1) {
if (!err)
if (errno == EADDRINUSE || errno == EADDRNOTAVAIL || errno == EACCES)
err = errno;
close(service_fd);
service_fd = -1;
continue;
}
if (listen(service_fd, 5) == -1) {
if (!err)
if (errno == EADDRINUSE)
err = errno;
close(service_fd);
service_fd = -1;
continue;
}
/* This socket works. */
break;
}
freeaddrinfo(list);
list = curr = NULL;
if (service_fd == -1) {
if (err)
fprintf(stderr, "Cannot listen for incoming connections on the specified host and port: %s.\n", strerror(err));
else
fprintf(stderr, "Cannot listen for incoming connections on the specified host and port.\n");
return EXIT_FAILURE;
}
/* Do not leak the listening socket to child processes. */
fcntl(service_fd, F_SETFD, FD_CLOEXEC);
/* We also want the listening socket to be nonblocking. */
fcntl(service_fd, F_SETFL, O_NONBLOCK);
fprintf(stderr, "Process %ld is waiting for incoming TCP connections.\n", (long)getpid());
/* Incoming connection loop. */
while (!done) {
struct timeval t;
char client_host[64]; /* 64 for numeric, 1024 for non-numeric */
char client_port[32];
pid_t client_pid;
fd_set fds;
t.tv_sec = 0;
t.tv_usec = 100000; /* Max. 0.1s delay to react to done signal. */
FD_ZERO(&fds);
FD_SET(service_fd, &fds);
if (select(service_fd + 1, &fds, NULL, NULL, &t) < 1)
continue;
client_addrlen = sizeof client_addr;
client_fd = accept(service_fd, (struct sockaddr *)&client_addr, &client_addrlen);
if (client_fd == -1) {
if (errno == EINTR || errno == ECONNABORTED)
continue;
fprintf(stderr, "Error accepting an incoming connection: %s.\n", strerror(errno));
continue;
}
if (getnameinfo((const struct sockaddr *)&client_addr, client_addrlen,
client_host, sizeof client_host, client_port, sizeof client_port,
NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
fprintf(stderr, "Cannot resolve peer address for incoming connection, so dropping it.\n");
close(client_fd);
continue;
}
printf("Incoming connection from %s:%s", client_host, client_port);
fflush(stdout);
if (peer_pids(client_fd, &client_pid, 1) != 1) {
printf(", but cannot determine process ID. Dropped.\n");
close(client_fd);
continue;
}
printf(" from local process %ld.\n", (long)client_pid);
fflush(stdout);
/*
* Handle connection.
*/
printf("Closing connection.\n");
fflush(stdout);
close(client_fd);
}
/* Close service socket. */
close(service_fd);
switch (__atomic_load_n(&done, __ATOMIC_SEQ_CST)) {
case SIGINT:
fprintf(stderr, "Received INT signal.\n");
break;
case SIGHUP:
fprintf(stderr, "Received HUP signal.\n");
break;
case SIGTERM:
fprintf(stderr, "Received TERM signal.\n");
break;
}
return EXIT_SUCCESS;
}
The peer_pids() function communicates with the helper process. It is very straightforward, albeit careful to not return unreliable data: instead of ignoring errors or trying to recover from them, it reports failure. This allows the main program do if (peer_pids(client_fd, &pid, 1) != 1) /* Don't know! */ and drop any connection the server is unsure of -- an approach I consider the sane one here.
The normalfds() helper function is often ignored. It helps avoid issues if any of the standard streams are/get closed. It simply moves the set of descriptors away from the three standard streams, using at most three extra descriptors.
You can define USE_SUDO at compile time to have it use sudo when executing the helper. Define HELPER_PATH and HELPER_NAME to the absolute path to the helper and its file name, respectively. (As it is now, they default to ./tcp-peer-pid and tcp-peer-pid, for easier testing.)
The server does install a signal handler for INT (Ctrl+C), HUP (sent when the user closes the terminal), or TERM signals, which all cause it to stop accepting new connections and exit in a controlled manner. (Because the signal handler is installed using SA_RESTART flag, its delivery will not interrupt slow syscalls or cause errno == EINTR. This also means that accept() should not block, or the signal delivery will not be noticed. So, blocking in select() for 0.1s, and checking if a signal was delivered in between, is a good compromise, at least in an example server.)
On my machine, I compiled and tested the service in one terminal window using
gcc -Wall -O2 tcp-peer-pids.c -o tcp-peer-pids
gcc -Wall -O2 "-DHELPER_PATH=\"$PWD/tcp-peer-pids\"" server.c -o server
./server - 2400
That will report Process # is waiting for incoming TCP connections. In another window, using Bash or POSIX shell, I run one or more test netcat commands:
nc localhost 2400 & wait
It might look silly to run a command in the background, and immediately wait for it, but that way you can see the PID of the nc process.
On my system, all loopback (127.x.y.z), TCP/IPv4, and TCP/IPv6 (the addresses of my ethernet and WiFi interfaces) worked fine, and reliably reported the correct PID of the process connecting to the example server.
There are a number of cases where the number of PIDs reported might vary: For example, if the program has executed a child process, but left the connected descriptor open in the child as well. (This should be considered a bug.) Another typical case is the program having exited before the netstat command executes.
If you find any typos or errors or strange behaviour, let me know in a comment so I can verify and fix. I wrote both programs in one sitting, so they are quite likely to contain bugs. As I mentioned, I would not trust either in production before having a colleague (or myself a few times, later on, with fresh eyes) going through it with a critical/paranoid eye.
I would personally only use this approach for logging and statistics, not access control per se. By access control, I mean that you should configure an IP filter (the firewall built in to the Linux kernel) to limit access to only trusted hosts; and specifically allow no incoming proxy connections to the proxy service if only local applications are to be proxied, rather than rely on this detecting all remote connections.
For application-specific logging/limiting, use readlink() on the /proc/PID/exe pseudosymlink. This cannot be faked, but the call may fail if the executable is not accessible, or is too deep in the directory tree. (In those cases I'd reject the proxy connection altogether.)
Note that it is usually trivial for an user to copy an executable to any directory they own, and execute it from there. This means that for application-specific limiting to work at all, you should have tight limits for all applications by default, and relax the limits for specific executables.
I'm trying to teach myself about multi-threading and multi-process programming in C (Linux). I wrote a short program which spawns a new thread which goes to a routine that tries to do a blocking read from an empty FIFO, while the main thread continues and prints to STDOUT. (Note: I did create a FIFO using mkfifo newfifo in my terminal before executing the program)
I was expecting the program to print to screen "Main thread", and then block while waiting for me to put data in the FIFO. Instead, the entire process is blocked, and the message "Main thread" only prints after I've put data into the FIFO.
Am I missing something here? Shouldn't the main thread continue to run even though the spawned thread is blocked? I tried a test using fork and creating a child process and got the same result (both processes blocked by read from empty FIFO).
Code is below:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <pthread.h>
#define NEWPIPE "./newfifo"
typedef struct __attribute__ ((__packed__)) {
int reserved :30;
int threadStarted :1;
int msgRcved :1;
} Status;
void* thread_routine(int fd, char* buffer, Status* myStatus)
{
int great_success = 0;
myStatus->threadStarted = 1;
printf("Side thread\n");
great_success = read(fd, buffer, sizeof(buffer));
if (great_success < 0) {
printf("pipe failed\n");
} else {
myStatus->msgRcved = 1;
}
}
void main()
{
int fd;
int cnt = 0;
char buffer[20];
Status* myStatus;
pthread_t thread_id;
myStatus = (Status*) malloc(sizeof(Status));
myStatus->reserved = 0;
myStatus->threadStarted = 0;
myStatus->msgRcved = 0;
fd = open(NEWPIPE, O_RDONLY);
pthread_create(&thread_id,
NULL,
(void *) thread_routine(fd, buffer, myStatus),
NULL);
printf("Main thread\n");
while (!myStatus->threadStarted) {
printf("Main thread: side thread started!\n");
}
while (!myStatus->msgRcved) {
sleep(1);
cnt++;
}
printf("buffer (cnt = %d): %s\n", cnt, buffer);
}
Edit: Latest Code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <pthread.h>
#define NEWPIPE "./newfifo"
struct Status {
unsigned int reserved :30;
unsigned int threadStarted :1;
unsigned int msgRcved :1;
};
void* thread_routine(void *arg)
{
int great_success = 0;
int fd;
char buffer[20];
struct Status* myStatus;
fd = open(NEWPIPE, O_RDONLY);
myStatus = arg;
myStatus->threadStarted = 1;
printf("Side thread\n");
while (1) {
great_success = read(fd, buffer, 20);
if (great_success < 0) {
printf("pipe failed\n");
} else {
printf("buffer : %s\n", buffer);
printf("great_success = %d\n", great_success);
great_success = 0;
}
}
}
void main()
{
int cnt = 0;
struct Status* myStatus;
pthread_t thread_id;
myStatus = (struct Status*) malloc(sizeof(struct Status));
myStatus->reserved = 0;
myStatus->threadStarted = 0;
myStatus->msgRcved = 0;
pthread_create(&thread_id,
NULL,
&thread_routine,
(void *) myStatus); // arguments to pass to the function!
printf("Main thread\n");
while (!myStatus->msgRcved) {
printf("Main thread: side thread started!\n");
if (myStatus->threadStarted) {
printf("spawned thread started!\n");
}
sleep(1);
}
pthread_exit(NULL);
}
You are passing the result of calling thread_routine() to pthread_create(). The arguments must all be evaluated before the call is executed, so the thread does not get created until that function returns. Probably. Because the thread_routine() does not return a (*)(void *), but pthread_create() attempts to call the return value as if it were one, the whole program's behavior is undefined. You want to pass a pointer to the function, not the result of calling it:
pthread_create(&thread_id,
NULL,
thread_routine,
NULL);
"But what about the arguments," you ask? That leads me to the next point: function thread_routine() does not have the correct signature for a thread start routine. A thread start routine must accept a single argument of type void *. The last argument to pthread_create() will be passed to the specified function as its (single) argument, and you can make that a pointer to an appropriate struct in lieu of passing multiple separate arguments.
Finally, your putative thread start function needs to exit either by returning a pointer value (possibly NULL) or by calling pthread_exit(). Behavior is undefined when a value-returning function other than main() reaches its terminal } without executing a return statement. (pthread_exit() solves that because it does not return.)
Note, by the way, that your compiler ought to have spit out several warnings about this code. You should always resolve all compiler warnings, or be certain why it's safe not to do so.
Instead, the entire process is blocked, and the message "Main thread" only prints after I've put data into the FIFO.
Am I missing something here?
Your main thread is blocked at this line:
fd = open(NEWPIPE, O_RDONLY);
because a non-blocking, read-only open of a FIFO will block until a writer is available. Your main thread is finally unblocked, not when you write data to the FIFO, but when you simply open the FIFO for writing.
There are other problems in the code as #JohnBollinger discusses in his answer. However, the FIFO semantics are why you are not seeing the initial output you expect.
the following is a way to open a named pipe,
so there is no need for any (before running application)
processing needed.
enum enumReturnStatus create_Pipe( INT32 taskSelector )
{
enum enumReturnStatus returnStatus = eRS_Success; // indicate success
char *pTask_NameString = NULL;
char Pipe_NameString[ 100 ] = {'\0'};
struct stat statInfo; // will contain info on a file
// and is used to determine if the pipe already exists
INT32 mkfifoStatus = 0;
INT32 statStatus = 0;
if( 0 >= Pipe_Parameters[ taskSelector ].Pipe_FD )
{ // then pipe not open
pTask_NameString = get_pTask_NameString( taskSelector );
if( NULL == pTask_NameString )
{ // task not configured
return( eRS_Failure );
}
/* *********************************************************************
* implied else, task configured
* ********************************************************************
*/
// create pipe name string
sprintf( Pipe_NameString, "/tmp/Pipe_2%s", pTask_NameString );
// check if pipe already exists
statStatus = stat(Pipe_NameString, &statInfo);
if( (statStatus)&&(ENOENT == errno) )
{ // then, FIFO pipe does not exist
// create the pipe
umask(0);
// maybe use mknode() instead of mkfifo()
// mknod(pPipe_name, S_IFIFO | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0 );
// 0666 allows anyone to read/write the pipe
mkfifoStatus = mkfifo( Pipe_NameString, 0666 );
if ( -1 == mkfifoStatus )
{
CommonWriteCdsLog( eLL_Critical,
get_pFormatString( eFormat_CFFL_string_string ),
__FILE__, __LINE__,
"LibFunc:mkfifo() failed to create pipe ",
strerror( errno ) );
fprintf(stderr,"mkfifo failed: %s \n",strerror( errno ));
fflush(stderr);
system( "sync; sync;" );
exit( EXIT_FAILURE );
}
} // endif ( pipe doesn't exist )
if( !mkfifoStatus && !statStatus )
{ // then pipe created or already exists
if( 0 >= Pipe_Parameters[taskSelector].Pipe_FD )
{ // then, pipe not yet open
// note: use O_RDWR so open will not hang
Pipe_Parameters[taskSelector].Pipe_FD = open( Pipe_NameString, O_CREAT|O_RDWR );
if( 0 >= Pipe_Parameters[taskSelector].Pipe_FD )
{ // then open failed
CommonWriteCdsLog( eLL_Critical,
get_pFormatString( eFormat_CFFL_string_string ),
__FILE__, __LINE__,
"LibFunc:open() failed for pipe",
strerror( errno ) );
}
else
{ // else open successful
;
} // endif( open for read successful )
} // endif( pipe not already open )
} // endif( pipe created or already exists )
} // endif( pipe not open )
return( returnStatus );
} // end create_Pipe()
This code:
typedef struct __attribute__ ((__packed__)) {
int reserved :30;
int threadStarted :1;
int msgRcved :1;
} Status;
will give problems as the code is using signed values and struct definitions should not be typedef'd as the typedef'ing:
obscures the code,
generates confusion in the reader
and clutters the compiler name space
here is an example of the preferred method to define a struct
(and correct the problem with the bit fields)
struct status
{
unsigned int reserved :30;
unsigned int threadStarted :1;
unsigned int msgRcved :1;
};
There should be no need for the packed attribute as all the bits will fit into a single unsigned int memory area. Reference this struct via: struct status in variable definitions and function parameter lists.
Here I have to send and receive dynamic data using a SysV message queue.
so in structure filed i have dynamic memory allocation char * because its size may be varies.
so how can i receive this type of message at receiver side.
Please let me know how can i send dynamic length of data with message queue.
I am getting problem in this i posted my code below.
send.c
/*filename : send.c
*To compile : gcc send.c -o send
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct my_msgbuf {
long mtype;
char *mtext;
};
int main(void)
{
struct my_msgbuf buf;
int msqid;
key_t key;
static int count = 0;
char temp[5];
int run = 1;
if ((key = ftok("send.c", 'B')) == -1) {
perror("ftok");
exit(1);
}
printf("send.c Key is = %d\n",key);
if ((msqid = msgget(key, 0644 | IPC_CREAT)) == -1) {
perror("msgget");
exit(1);
}
printf("Enter lines of text, ^D to quit:\n");
buf.mtype = 1; /* we don't really care in this case */
int ret = -1;
while(run) {
count++;
buf.mtext = malloc(50);
strcpy(buf.mtext,"Hi hello test message here");
snprintf(temp, sizeof (temp), "%d",count);
strcat(buf.mtext,temp);
int len = strlen(buf.mtext);
/* ditch newline at end, if it exists */
if (buf.mtext[len-1] == '\n') buf.mtext[len-1] = '\0';
if (msgsnd(msqid, &buf, len+1, IPC_NOWAIT) == -1) /* +1 for '\0' */
perror("msgsnd");
if(count == 100)
run = 0;
usleep(1000000);
}
if (msgctl(msqid, IPC_RMID, NULL) == -1) {
perror("msgctl");
exit(1);
}
return 0;
}
receive.c
/* filename : receive.c
* To compile : gcc receive.c -o receive
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct my_msgbuf {
long mtype;
char *mtext;
};
int main(void)
{
struct my_msgbuf buf;
int msqid;
key_t key;
if ((key = ftok("send.c", 'B')) == -1) { /* same key as send.c */
perror("ftok");
exit(1);
}
if ((msqid = msgget(key, 0644)) == -1) { /* connect to the queue */
perror("msgget");
exit(1);
}
printf("test: ready to receive messages, captain.\n");
for(;;) { /* receive never quits! */
buf.mtext = malloc(50);
if (msgrcv(msqid, &buf, 50, 0, 0) == -1) {
perror("msgrcv");
exit(1);
}
printf("test: \"%s\"\n", buf.mtext);
}
return 0;
}
A couple of ways to solve your problem are:
Make the messages fixed length.
Send a fixed length "header" that includes the message length.
Send a terminator, since you seem to send strings include the terminating '\0'.
Edit: How to use msgsnd and msgrcv:
Your usage of the structure and msgsnd is wrong, as the function expects the whole message to be one continuous memory area. Examples such as this use a structure with normal fields in it, or like this (at the bottom) which uses a fixed length string array.
You can send dynamic data having the structure size being dynamic as well. The trick here is to use a small fixed-size structure, and allocate more data than is needed.
Lets rewrite parts of your example sender code:
struct my_msgbuf {
long mtype; /* Message type, must be > 0 */
char mtext[1]; /* Some compilers allow `char mtext[0]` */
};
/* ... */
int count = 0;
while (count < 100) {
count++;
/* Put string in a temporary place */
char tmp[64];
snprintf(tmp, sizeof(tmp), "Hi hello test message here %d", count);
/* +1 for the terminating '\0' */
size_t msgsz = strlen(tmp) + 1;
/* Allocate structure, and memory for the string, in one go */
struct my_msgbuf *buf = malloc(sizeof(struct my_msgbuf) + msgsz);
/* Set up the message structure */
buf->mtype = 1;
memcpy(buf->mtext, tmp, msgsz);
/* And send the message */
msgsnd(msgid, buf, msgsz, IPC_NOWAIT);
/* Remember to free the allocated memory */
free(buf);
}
The above code handles sending of dynamic strings, as long as the are less than 63 characters (the size of the temporary string minus one).
Unfortunately msgrcv doesn't really support receiving of dynamically sized data. This can be helped by not using the MSG_NOERROR flag, and check for error E2BIG and then using realloc to get a bigger message buffer.
Something like this for receiving:
/* Should start with larger allocation, using small just for example */
size_t msgsz = 8;
struct my_msgbuf *buf = NULL;
for (;;) {
/* Allocate if `buf` is NULL, otherwise reallocate */
buf = realloc(buf, msgsz);
/* Receive message */
ssize_t rsz = msgrcv(msgid, buf, msgsz, 1, 0);
if (rsz == -1) {
if (errno == E2BIG)
msgsz += 8; /* Increase size to reallocate and try again */
else {
perror("msgrcv");
break;
}
} else {
/* Can use `buf->mtext` as a string, as it already is zero-terminated */
printf("Received message of length %d bytes: \"%s\""\n", rsz, buf->mtext);
break;
}
}
if (buf != NULL)
free(buf);
The above code for receiving only receives one single message. If you want it to match the sender which sends lots of messages, then put the receiving code in a function, and call it in a loop.
DISCLAIMER: This code is written directly in the browser, only reading the manual pages. I have not tested it.
I am testing kernel asynchronous io functions (not posix aio) and am trying to figure out how it works. The code below is a complete program where I simply write an array repeatedly to a file opened using O_DIRECT. I get an error in the callback function "write missed bytes expect 1024 got 0" (see the fprintf statement in work_done()).
For those not familiar with kernel aio, the code below does the following:
Init some structs
Prepare aio (io_prep_pwrite)
Submit io requests (io_submit)
Check for event completion (io_getevents)
Call a callback function to see if everything went ok.
I get an error at step 5. If I do not open the file using O_DIRECT, things work fine, but it beats the purpose of having async writes.
Can someone tell me what I am doing wrong? Is this the correct usage of kernel aio, for example, is my use of callbacks correct? Are there any restrictions on the usage of O_DIRECT?
I compile using 'gcc -Wall test.c -laio'
Thanks in advance.
/*
* File: myaiocp.c
* Author: kmehta
*
* Created on July 11, 2011, 12:50 PM
*
*
* Testing kernel aio.
* Program creates a 2D matrix and writes it multiple times to create a file of desired size.
* Writes are performed using kernel aio functions (io_prep_pwrite, io_submit, etc.)
*/
#define _GNU_SOURCE
#define _XOPEN_SOURCE 600
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <pthread.h>
#include <fcntl.h>
#include <string.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <omp.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <libaio.h>
char ** buf;
long seg_size;
int seg_rows;
double total_size;
char * filename;
static int wait_count = 0;
void io_task();
void cleanup();
void allocate_2D_matrix(int[]);
int file_open(char *);
void wr_done(io_context_t ctx, struct iocb* iocb, long res, long res2);
int main(int argc, char **argv) {
total_size = 1048576; //1MB
seg_size = 1024; //1kB
seg_rows = 1024;
filename = "aio.out";
int dims[] = {seg_rows, seg_size};
allocate_2D_matrix(dims); //Creates 2D matrix
io_task();
cleanup();
return 0;
}
/*
* Create a 2D matrix
*/
void allocate_2D_matrix(int dims[2]) {
int i;
char *data;
//create the matrix
data = (char *) calloc(1, dims[0] * dims[1] * sizeof (char));
if (data == NULL) {
printf("\nCould not allocate memory for matrix.\n");
exit(1);
}
buf = (char **) malloc(dims[0] * sizeof (char *));
if (buf == NULL) {
printf("\nCould not allocate memory for matrix.\n");
exit(1);
}
for (i = 0; i < dims[0]; i++) {
buf[i] = &(data[i * dims[1]]);
}
}
static void io_error(const char *func, int rc)
{
if (rc == -ENOSYS)
fprintf(stderr, "AIO not in this kernel\n");
else if (rc < 0)
fprintf(stderr, "%s: %s\n", func, strerror(-rc));
else
fprintf(stderr, "%s: error %d\n", func, rc);
exit(1);
}
/*
* Callback function
*/
static void work_done(io_context_t ctx, struct iocb *iocb, long res, long res2)
{
if (res2 != 0) {
io_error("aio write", res2);
}
if (res != iocb->u.c.nbytes) {
fprintf(stderr, "write missed bytes expect %lu got %ld\n",
iocb->u.c.nbytes, res2);
exit(1);
}
wait_count --;
printf("%d ", wait_count);
}
/*
* Wait routine. Get events and call the callback function work_done()
*/
int io_wait_run(io_context_t ctx, long iter)
{
struct io_event events[iter];
struct io_event *ep;
int ret, n;
/*
* get up to aio_maxio events at a time.
*/
ret = n = io_getevents(ctx, iter, iter, events, NULL);
printf("got %d events\n", n);
/*
* Call the callback functions for each event.
*/
for (ep = events ; n-- > 0 ; ep++) {
io_callback_t cb = (io_callback_t)ep->data ; struct iocb *iocb = ep->obj ; cb(ctx, iocb, ep->res, ep->res2);
}
return ret;
}
void io_task() {
long offset = 0;
int bufIndex = 0;
//Open file
int fd = file_open(filename);
//Initialize structures
long i;
long iter = total_size / seg_size; //No. of iterations to reach desired file size (total_size)
io_context_t myctx;
if(0 != io_queue_init(iter, &myctx))
{
perror("Could not initialize io queue");
exit(EXIT_FAILURE);
}
struct iocb * ioq[iter];
//loop through iter times to reach desired file size
for (i = 0; i < iter; i++) {
struct iocb *io = (struct iocb*) malloc(sizeof (struct iocb));
io_prep_pwrite(io, fd, buf[bufIndex], seg_size, offset);
io_set_callback(io, work_done);
ioq[i] = io;
offset += seg_size;
bufIndex ++;
if (bufIndex > seg_rows - 1) //If entire matrix written, start again from index 0
bufIndex = 0;
}
printf("done preparing. Now submitting..\n");
if(iter != io_submit(myctx, iter, ioq))
{
perror("Failure on submit");
exit(EXIT_FAILURE);
}
printf("now awaiting completion..\n");
wait_count = iter;
int res;
while (wait_count) {
res = io_wait_run(myctx, iter);
if (res < 0)
io_error("io_wait_run", res);
}
close(fd);
}
void cleanup() {
free(buf[0]);
free(buf);
}
int file_open(char *filename) {
int fd;
if (-1 == (fd = open(filename, O_DIRECT | O_CREAT | O_WRONLY | O_TRUNC, 0666))) {
printf("\nError opening file. \n");
exit(-1);
}
return fd;
}
First of all, good job using libaio instead of POSIX aio.
Are there any restrictions on the usage of O_DIRECT ?
I'm not 100% sure this is the real problem, but O_DIRECT has some requirements (quoting mostly from TLPI):
The data buffer being transferred must be aligned on a memory boundary that is a multiple of the block size (use posix_memalign)
The file or device offset at which data transfer commences must be a multiple of the block size
The length of the data to be transferred must be a multiple of the block size
At a glance, I can see you are not taking aby precautions to align memory in allocate_2D_matrix.
If I do not open the file using O_DIRECT, things work fine, but it
beats the purpose of having async writes.
This happens not to be the case. Asynchronous I/O works well without O_DIRECT (for instance think of the number of system calls slashed).