number of hard links to a empty pipe linux vs unix - c

This is what I found when I was working on one of my course projects. Below is the C code block to print out the information about an empty pipe which has not connected to any process yet.
{
int pfd[2], nread;
char s[100];
struct stat pipe_info;
if (pipe(pfd) == -1)
{
perror ("pipe");
return (-1);
}
if (fstat (pfd[0], &pipe_info) < 0)
perror ("fstat");
print_info (&pipe_info);
if (fstat (pfd[1], &pipe_info) < 0)
perror ("fstat");
print_info (&pipe_info);
return(0);
}
void print_info (struct stat *pipe_info)
{
printf ("mode %o\n", pipe_info->st_mode);
printf ("inode %d\n", pipe_info->st_ino);
printf ("device %d\n", pipe_info->st_dev);
printf ("minor device %d\n", pipe_info->st_rdev);
printf ("num links %d\n", pipe_info->st_nlink);
printf ("uid %d\n", pipe_info->st_uid);
printf ("gid %d\n", pipe_info->st_gid);
printf ("size %d\n", pipe_info->st_size);
printf ("atime %d\n", pipe_info->st_atime);
printf ("mtime %d\n", pipe_info->st_mtime);
printf ("ctime %d\n", pipe_info->st_ctime);
printf ("block size %d\n", pipe_info->st_blksize);
printf ("block %d\n", pipe_info->st_blocks);
}
I compiled the source code on both a Linux machine and a Solaris OS machine. What I found was that on the Linux machine, the number of links is 1 while on the Solaris OS machine, the number of links for the pipe is 0. I am fairly new to the kernels of both systems and would like to know why the number of links are different on the two systems?

The SunOS 5.10 / Solaris 2.x manual says this about the st_nlink field:
st_nlink This field should be used only by administrative commands.
which I read as "this field has a nonsensical value".
Contrariwise, the value for Linux makes sense: the pipe has a link to the process that created it. I expect that st_nlink would equal 2 once the other side was conected to a (forked) process. The Linux fstat claims POSIX compliance which is good. The Solaris man page I have makes no such claims.
If your underlying question is how can I tell if the farside of a pipe is connected, there are two answers:
Your program should know if it attached the farside
You can try to write the pipe and get some combination of EAGAIN, EWOULDBLOCK, EPIPE, or the SIGPIPE signal.
Option 2 would be problematic if the other side of the pipe is connected. You could work around it if you can create a message that would never be sent by the writer to be rejected by the reader.

Related

Why does gdb mi give me &"\n" as return to my -gdb-exit command?

I am writing some code that has to communicate with gdb mi. So I forked my programm, put two pipes in place and started gdb mi in the child, so that I can communicate with gdb mi from the parent.
gdb always returns "(gdb) \n" when it is finished, so I look for that and write my next command. This is a minimum example of my code:
int main(){
printf("Starting Test\n");
int fromGDB[2], toGDB[2], nbytes;
pid_t childpid;
char readbuffer[80] = "";
pipe(fromGDB);
pipe(toGDB);
if((childpid = fork())==-1)
{
perror("fork");
exit(1);
}
if(childpid == 0){
close(toGDB[1]);
close(fromGDB[0]);
int backup = dup(1); //store stdout
if (dup2(fromGDB[1],1) < 0){puts("hat nicht geklappt");}
int backupStdin = dup(0); //store stdin
if (dup2(toGDB[0],0) < 0){puts("hat nicht geklappt");}
system("gdb -q --interpreter=mi2"); // start gdb mi
dup2(backup,1); // restore stdout
puts("child fertig");
exit(0);
}else{
close(toGDB[0]);
close(fromGDB[1]);
char* writeCommand = "";
int commandCounter = 0;
while (commandCounter <3){
nbytes = read(fromGDB[0],readbuffer,sizeof(readbuffer));
printf("parent recived: %s", readbuffer);
if (strncmp(readbuffer+strlen(readbuffer)-strlen("(gdb) \n"),"(gdb) \n", strlen("(gdb) \n")) == 0){
switch (commandCounter){
case 0: writeCommand = "-file-exec-and-symbols /home/dev/spielwiese/build/main\n"; break;
case 1: writeCommand = "-gdb-exit\n"; break;
default: writeCommand = "you should never reach here\n"; break;
}
write(toGDB[1],writeCommand,strlen(writeCommand)+1);
printf("wrote: %s", writeCommand);
commandCounter++;
}else if(strncmp(readbuffer,"^exit", sizeof("^exit")-1) == 0){
break;
}
memset(readbuffer,'\0',strlen(readbuffer)); //clear the readbuffer
}
puts("parent fertig");
sleep(5);
}
return 0;
}
If I call the same commands by hand, thats the output I get (-> means input from me)
-> gdb -q --interpreter=mi2
=thread-group-added,id="i1"
(gdb)
-> -file-exec-and-symbols /home/dev/spielwiese/build/main
^done
(gdb)
-> -gdb-exit
^exit
But if I run my code, which should be essentialy the same, I get this output:
Starting Test
parent recived: =thread-group-added,id="i1"
parent recived: (gdb)
wrote: -file-exec-and-symbols /home/dev/spielwiese/build/main
parent recived: ^done
(gdb)
wrote: -gdb-exit
parent recived: &"\n"
parent recived: ^done
(gdb)
wrote: you should never reach here
parent fertig
According to the gdb mi manual, an & preceeds a log entry, but this log entry is empty, exept for the newline. Also, I don't know why there should be a log entry for exiting, or why it fails to exit, but doesen't produce an error.
Also, if you know any better sources for gdb mi than this: https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html#GDB_002fMI , please let me know.
There are several issues with your example code, many of which have already been summarized in comments:
read() and write() transfer only the bytes specified, or a leading subsequence of those. They do not append null bytes to the transfer.
read() and write() are not guaranteed to transfer the full number of bytes requested on any given call.
your buffer might not be long enough to accommodate some responses from the child all in a single read().
According to the gdb mi manual, an & preceeds a log entry, but this log entry is empty, exept for the newline. Also, I don't know why there should be a log entry for exiting, or why it fails to exit, but doesen't produce an error.
None of the above explain the log entry or other behavior differences, but this probably does:
write(toGDB[1],writeCommand,strlen(writeCommand)+1);
Supposing that the full number of bytes requested is transferred, you are writing not only the command, but also a string terminator. The terminator is not part of the MI protocol, so your program is not behaving the same as your interactive session. Moreover, the particular error -- an extra null byte -- is one that is especially likely to produce mysterious output. I can only speculate about the specifics, but I could believe that gdb is treating the extra null byte immediately following a newline as if it terminated a zero-byte command. If so, then you are effectively issuing three commands to gdb, not two, and the gdb log output is about the null byte / empty command.
You would probably find stream I/O to be more convenient for your purposes than raw I/O. That would insulate you from many of the idiosyncrasies of raw I/O, and it would make fgets() available to you for input, which would be to your advantage. The fdopen() function can wrap streams around your file descriptors:
#define BUFFER_SIZE 1024
// Parent
FILE *fFromGDB = fdopen(fromGDB[0], "r");
FILE *fToGDB = fdopen(fToGDB[1], "w");
if (!fFromGDB || ! fToGDB) // ...
// You probably want fToGDB to be line-buffered, not block buffered:
setvbuf(fToGDB, NULL, _IOLBF, BUFFER_SIZE);
Then use fputs(), fgets(), fprintf(), etc. to interact with the child.
Also, if you know any better sources for gdb mi than this: https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html#GDB_002fMI , please let me know.
Requests for external resources are off-topic here.
In any event, you are referencing the appropriate manual, and short of analyzing the source code, that is the ultimate source of knowledge on the matter.

Segmentation core error c

I am little bit ruggy with C coding, but I need this script working to test serial communication with a microcontroller. I have the following code written as:
int main() {
char *portname = "/dev/ttyACM0";
FILE *csv = fopen("~/Desktop/my.csv", "wb");
int fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0)
{
//error_message ("error %d opening %s: %s", errno, portname, strerror (errno));
return 1;
}
set_interface_attribs (fd, B9600, 0); // set speed to 9600 bps, 8n1 (no parity)
set_blocking (fd, 1); // set no blocking
char a = 255;
int i = 0;
write(fd, &a, 1);
do {
char c;
read(fd, &c, 1); // wait for next value
fprintf(csv, "%d\n", c);
i ++;
} while(i < 10000); //keep running this loop for a while
write(fd, &a, 1);
fclose(csv);
close(fd);
return 0;
This scripts should connect successfully for serial communication, send a start bit (255) to the receiver which operates some actions, and then start writing received data on csv file until the while loop ends up. Have compiled that source with the instruction:
g++ -o serial c-serial.c
where c-serial.c is the name of the source code. Just skip the two "set" function, they are visible in this scope so the error comes not out of this. In fact, when executing I receive:
Segmentation fault (core dumped)
How do I fix it?
Function call fopen is not able to resolve ~ as your home directory. Please take a look at how to open a file in user's home folder.
In your fprintf statement you tell the programm to print an integer (usually two bytes) but provide a char variable (one byte) to print from. fprintf relies on the supplied format string to decide how many bytes to read from the source. In this case fprintf tries to read (at least) two bytes from a one-byte-memory block. Therefore fprintf will try to access memory which might not even be allocated to the program, producing a segmentation fault.
Correct the format string to print a character only.
For more information on fprintf and format strings see here.

C: not able to open message queue

I'm working on OpenSuse 42.3 Leap. This is my first touch with Unix message queues and I have some basic trouble with simple opening a new queue. My original issue was I wasn't able to open two queues but after few tries I got the problem reduced to this strange behaviour:
If I compile and run this
#include<stdio.h>
#include <mqueue.h>
int main() {
// printf("Hello world!\n");
/* create queue */
char *q_name = "/myQueue";
mqd_t desc = mq_open(q_name, O_RDWR | O_CREAT);
if(desc == (mqd_t) -1)
perror("Error in mq_open");
printf("We have opened %d\n", desc);
/* close descriptor and unlink name */
if (mq_close(desc)) perror("Error in close:");
if (mq_unlink(q_name)) perror("Error in unlink:");
return 0;
}
it works great with standard output:
We have opened 3
The queue is closed correctly and I can rerun it with no error.
But if I uncomment the line
printf("Hello world!\n");
it obviously still correctly compiles but when run it outputs
Hello world!
Error in mq_open: Invalid argument
We have opened -1
Error in close:: Bad file descriptor
Error in unlink:: No such file or directory
If instead of simple 'Hello world! I try to print:
printf("Hello world! My pid = %d\n", getpid());
then instead of Invalid argument the error
Error in mq_open: Bad address
is produced.
Any idea why this simple printf crashes the queue opening?
From the mq_open manual page:
If O_CREAT is specified in oflag, then two additional arguments must
be supplied. [...]
You don't supply them, so you have undefined behaviour. What seems to happen is that the missing arguments are taken from somewhere in memory where they would have been, and what happens to be there is different depending on what your program did just before.

MINIX 3 - server print information

I am trying to modify the sched server and have been attempting to have it print out some information to stdout or stderr but without luck. I tried printf, fprintf, sprintf, puts, fputs. Any ideas on how I should do this? I am starting to think that for whatever reason it's not possible to have the server print something out even though it has printfs in it.
I tried adding these in schdule_process, start_scheduling and balance_queues.
#include <stdio.h>
static void balance_queues(minix_timer_t *tp)
{
struct schedproc *rmp;
int proc_nr;
printf("Test prinf\n");
fprintf(stdout, "Test fprintf stdout\n");
fprintf(stderr, "Test fprintf stderr\n");
puts("Test puts\n");
fputs("Test fputs stdout", stdout);
fputs("Test fputs stderr", stderr);
for (proc_nr=0, rmp=schedproc; proc_nr < NR_PROCS; proc_nr++, rmp++) {
if (rmp->flags & IN_USE) {
if (rmp->priority > rmp->max_priority) {
rmp->priority -= 1; /* increase priority */
schedule_process_local(rmp);
}
}
}
set_timer(&sched_timer, balance_timeout, balance_queues, 0);
}
Here's the balance_queues function I tried. I am pretty sure this is not a problem with my code instead something with minix which I don't yet understand. I've spend the last 2 hours reading and searching Tanenbaums and Woodhulls "The Minix book - Operating Systems, design and implementation" third edition but couldn't find anything. From what I understand this function should be called every 5 seconds to balance the queues but I there's nothing being printed to the command line!

stdio to terminal after close(STDOUT_FILENO) behavior

I am wondering why uncommenting that first printf statement in the following program changes its subsequent behavior:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main() {
//printf("hi from C \n");
// Close underlying file descriptor:
close(STDOUT_FILENO);
if (write(STDOUT_FILENO, "Direct write\n", 13) != 13) // immediate error detected.
fprintf(stderr, "Error on write after close(STDOUT_FILENO): %s\n", strerror(errno));
// printf() calls continue fine, ferror(stdout) = 0 (but no write to terminal):
int rtn;
if ((rtn = printf("printf after close(STDOUT_FILENO)\n")) < 0 || ferror(stdout))
fprintf(stderr, "Error on printf after close(STDOUT_FILENO)\n");
fprintf(stderr, "printf returned %d\n", rtn);
// Only on fflush is error detected:
if (fflush(stdout) || ferror(stdout))
fprintf(stderr, "Error on fflush(stdout): %s\n", strerror(errno));
}
Without that first printf, the subsequent printf rtns 34 as if no error occured even though the connection from the stdout user buffer to the underlying fd has been closed. Only on a manual fflush(stdout) does the error get reported back.
But with that first printf turned on, the next printf reports errors as I would expect.
Of course nothing is written to the terminal(by printf) after the STDOUT_FILENO fd has been closed in either case.
I know it's silly to close(STDOUT_FILENO) in the first place here; this is an experiment I stumbled into and thinking someone more knowledgeable in these areas may see something instructive to us in it..
I am on Linux with gcc.
If you strace the both programs, it seems that stdio works so that upon first write, it checks the descriptor with fstat to find out what kind of file is connected to the stdout - if it is a terminal, then stdout shall be line-buffered, if it is something else, then stdout will be made block-buffered. If you call close(1); before the first printf, now the initial fstat will return EBADF and as 1 is not a file descriptor that points to a character device, stdout is made block-buffered.
On my computer the buffer size is 8192 bytes - that many bytes can be buffered to be written to stdout before the first failure would occur.
If you uncomment the first printf, the fstat(1, ...) succeeds and Glibc detects that stdout is connected to a terminal; stdout is set to line-buffered, and thus because printf after close(STDOUT_FILENO)\n ends with newline, the buffer will be flushed right away - which will result in an immediate error.

Resources