For my OS class I'm supposed to implement Linux's cat using only system calls (no printf)
Reading this reference I found it being used to print to a file. I guess I should manipulate ofstream.
In the example appears: ofstream outfile ("new.txt",ofstream::binary);
How can I make it write to the screen?
EDIT: I realized this write() is part of iostream library, is this the same as the int write (int fd, char *buf , int size) system call?
A system call is a service provided by Linux kernel. In C programming, functions are defined in libc which provide a wrapper for many system calls. The function call write() is one of these system calls.
The first argument passed to write() is the file descriptor to write to. The symbolic constants STDERR_FILENO, STDIN_FILENO, and STDOUT_FILENO are respectively defined to 2, 0, and 1 in unidtd.h. You want to write to either STDOUT_FILENO or STDERR_FILENO.
const char msg[] = "Hello World!";
write(STDOUT_FILENO, msg, sizeof(msg)-1);
You can alternatively use the syscall() function to perform an indirrect system call by specifying the function number defined in syscall.h or unistd.h. Using this method, you can guarantee that you are only using system calls. You may find The Linux System Call Quick Refernence (PDF Link) to be helpful.
/* 4 is the system call number for write() */
const char msg[] = "Hello World!";
syscall(4, STDOUT_FILENO, msg, sizeof(msg)-1);
No, std::ostream::write is not the same as the write system call. It does (almost certainly) use the write system call, at least on a system like Linux that has such a thing, and it normally does pretty similar things, but it's still a separate thing of its own.
Linux will, however, pre-open standard input, standard output and standard error streams for your process. To write to the screen, you'd normally use write (i.e., the one that is a system call) to write to stream number 1 or stream number 2 (which are standard output and standard error respectively).
If you need to write to the screen even if those are re-directed, you'd normally open a stream to /dev/tty and (again) use write to write to it:
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main() {
char msg[] = "hello\n";
int fd = open("/dev/tty", O_WRONLY);
write(fd, msg, sizeof(msg));
return 0;
}
#include <unistd.h>
/* ... */
const char msg[] = "Hello world";
write( STDOUT_FILENO, msg, sizeof( msg ) - 1 );
First argument is the file descriptor for STDOUT (usually 1), the second is the buffer to write from, third is the size of the text in the buffer (-1 is to not print zero terminator).
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <unistd.h> // For open, close, read, write, fsync
#include <sys/syscall.h> //For SYSCALL id __NR_xxx
//Method 1 : API
write(1,"Writing via API\n",\
strlen("Writing via API\n") );
fsync(1);
//Method 2 : Via syscall id
const char msg[] = "Hello World! via Syscall\n";
syscall(__NR_write, STDOUT_FILENO, msg, sizeof(msg)-1);
syscall(__NR_fsync, STDOUT_FILENO ); // fsync(STDOUT_FILENO);
Your reference is incorrect. It's part of C++ and has nothing to do with your assignment. The correct reference is http://www.opengroup.org/onlinepubs/9699919799/functions/write.html
Related
I'm trying to use posix_openpt on Mac. The issue I'm seeing is that I get a file descriptor back from posix_openpt. I use the file descriptor for reading and create a copy using dup for writing. The issue I'm running into is that when I write to the master file descriptor, I read that data back out from the master. So no data ends up at the slave. I confirmed this by using posix_spawnp to run a program with stdin/stdout/stderr set to the slave file. The program hangs indefinitely waiting for input. Here is my code (note, all error handling was removed for legibility):
int master_fd = posix_openpt(O_RDWR);
grantpt(master_fd);
unlockpt(master_fd);
char *slave_filename_orig = ptsname(master_fd);
size_t slave_filename_len = strlen(slave_filename_orig);
char slave_filename[slave_filename_len + 1];
strcpy(slave_filename, slave_filename_orig);
posix_spawn_file_actions_t fd_actions;
posix_spawn_file_actions_init(&fd_actions);
posix_spawn_file_actions_addopen(&fd_actions, STDIN_FILENO, slave_filename, O_RDONLY, 0644);
posix_spawn_file_actions_addopen(&fd_actions, STDOUT_FILENO, slave_filename, O_WRONLY, 0644);
posix_spawn_file_actions_adddup2(&fd_actions, STDOUT_FILENO, STDERR_FILENO);
pid_t pid;
posix_spawnp(&pid, "wc", &fd_actions, NULL, NULL, NULL);
int master_fd_write = dup(master_fd);
char *data = "hello world";
write(master_fd_write, data, strlen(data));
close(master_fd_write);
char buffer[1024];
read(master_fd, buffer, 1024); // <- Issue Here
// buffer now contains hello world. It should contain the output of `wc`
(Note: The above was only tested on Linux; I don't have a Mac to work on, but I have no reason to believe it's any different in the details here.)
There are several problems with your code:
At least on Linux, calling posix_spawn() with a null pointer causes a crash. You need to provide all the arguments. Even if Macs accept it the way you have it, doing this is a Good Idea.
Next, wc reading from standard input will wait until an attempt to read more data gives an End Of File condition before it prints out the statistics it gathers; your code doesn't do this. With a pty, if you write a specific byte (Typically with the value 4, but it can be different, so best to use what the terminal says instead of hardcoding it) to it, the terminal driver will recognize that as signalling EOF without having to close the master like you would when using a pipe (Making it impossible to read the output of wc).
Second, the terminal's default settings include echoing the input; that's what you're reading.
A cleaned up version that addresses these issues and more (Like yours, with most error checking omitted; real code should be checking all these functions for errors):
#define _XOPEN_SOURCE 700
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <spawn.h>
#include <termios.h>
#include <unistd.h>
#include <wait.h>
int main(void) {
int master_fd = posix_openpt(O_RDWR);
grantpt(master_fd);
unlockpt(master_fd);
char *slave_filename_orig = ptsname(master_fd);
size_t slave_filename_len = strlen(slave_filename_orig);
char slave_filename[slave_filename_len + 1];
strcpy(slave_filename, slave_filename_orig);
//printf("slave pty filename: %s\n", slave_filename);
// Open the slave pty in this process
int slave_fd = open(slave_filename, O_RDWR);
// Set up slave pty to not echo input
struct termios tty_attrs;
tcgetattr(slave_fd, &tty_attrs);
tty_attrs.c_lflag &= ~ECHO;
tcsetattr(slave_fd, TCSANOW, &tty_attrs);
posix_spawn_file_actions_t fd_actions;
posix_spawn_file_actions_init(&fd_actions);
// Use adddup2 instead of addopen since we already have the pty open.
posix_spawn_file_actions_adddup2(&fd_actions, slave_fd, STDIN_FILENO);
posix_spawn_file_actions_adddup2(&fd_actions, slave_fd, STDOUT_FILENO);
// Also close the master and original slave fd in the child
posix_spawn_file_actions_addclose(&fd_actions, master_fd);
posix_spawn_file_actions_addclose(&fd_actions, slave_fd);
posix_spawnattr_t attrs;
posix_spawnattr_init(&attrs);
pid_t pid;
extern char **environ;
char *const spawn_argv[] = {"wc" , NULL};
posix_spawnp(&pid, "wc", &fd_actions, &attrs, spawn_argv, environ);
close(slave_fd); // No longer needed in the parent process
const char *data = "hello world\n";
ssize_t len = strlen(data);
if (write(master_fd, data, len) != len) {
perror("write");
}
// Send the terminal's end of file interrupt
cc_t tty_eof = tty_attrs.c_cc[VEOF];
if (write(master_fd, &tty_eof, sizeof tty_eof) != sizeof tty_eof) {
perror("write EOF");
}
// Wait for wc to exit
int status;
waitpid(pid, &status, 0);
char buffer[1024];
ssize_t bytes = read(master_fd, buffer, 1024);
if (bytes > 0) {
fwrite(buffer, 1, bytes, stdout);
}
close(master_fd);
return 0;
}
When compiled and run, outputs
1 2 12
There are two problems with this code.
First, you are seeing "hello world" on master_fd because by default terminals echo. You need to set the terminal to raw mode to suppress that.
Second, wc won't output anything until it sees an EOF, and it will not see an EOF until you close the master. Not just master_fd_write mind you, but all copies of master_fd, including master_fd itself. However, once you close the master, you cannot read from it.
Choose some other program that wc to demonstrate the functionality of posix_openpt.
Edit: It is possible to raise the end-of-file condition on the slave without closing the master by writing ^D (EOT, ascii 4).
I wrote code below
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main() {
int fd = 3;
char c[100] = "Testing\n";
ssize_t nbytes = write(fd, (void *) c, strlen(c));
return 0;
}
compiled/linked, and executed
$ ./io
$ ./io 3> io_3.txt
The first line produced no output. The second line gave me file io_3.txt containing Testing.
This is all expected behaviour (I guess).
Even if in my tests it produced the expected output,
I am not certain if, to avoid potential problems, undefined behavior, etc., I should do anything prior to the first write, like checking if fd=3 is in use (and in that case, how... this may apply), if it is suitably open, etc.
And I am not certain if I should perform some action after the last write, for the same reasons.
Perhaps the way I did is "non-risky", the only potential issue being that nothing is written, which I could detect by checking the value of nbytes... I wouldn't know.
Any clarification is welcome.
If you write a program like this, executing it without fd 3 open is a usage bug. Normally the only file descriptors that should be used by number without having opened them yourself are 0 (stdin), 1 (stdout), and 2 (stderr). If a program needs to take additional pre-opened file descriptors as input, the standard idiom is to pass the fd numbers on the command line or environment variables rather than hard-coding them. For example:
int main(int argc, char **argv) {
if (argc<2 || !isdigit(argv[1][0])) return 1;
int fd = strtol(argv[1], 0, 0);
char c[100] = "Testing\n";
ssize_t nbytes = write(fd, (void *) c, strlen(c));
return 0;
}
In practice, a trivial program like yours is probably safe with the write just failing if fd 3 wasn't open. But as soon as you do anything that might open file descriptors (possibly internal to the implementation, like syslog, or date/time functions opening timezone data, or message translation catalogs, etc.), it might happen that fd 3 now refers to such an open file, and you wrongly attempt a write to it. Using file descriptors like this is a serious bug.
I am trying to write 2 programs that will talk to each other using fifo pipe.
I used the example here (section 5.2), but I changed the mknod there to mkfifo and tried to change gets to fgets.
This is the code (of one program which writes into the fifo):
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h> /*mkfifo, open */
#include <sys/wait.h>
#include <sys/stat.h> /* mkfifo, open */
#include <fcntl.h> /*open */
#define FIFO_PATH "/home/hana/Desktop"
#define BUFFER_SIZE 300
int main()
{
char buffer[BUFFER_SIZE];
int fd;
int wStatus;
mkfifo(FIFO_PATH, 666);
printf("waiting for readers\n");
fd = open(FIFO_PATH, O_RDWR);
while (fgets(buffer, BUFFER_SIZE, fd), !feof(stdin))
{
if ((wStatus = write(fd, buffer, strlen(buffer))) == -1)
perror("write");
else
printf("speak: wrote %d bytes\n", wStatus);
}
return 0;
}
I get a compilation error: passing argument 3 of fgets makes pointer from integer.
So fgets is expecting FILE* and not file descriptor.
What should I do? change something so that fgets works? use another function?
I am compiling with gcc (ansi, pedantic).
Thanks
The answer from whjm is the cause of your error diagnostic, but I think you probably meant
fgets(buffer, BUFFER_SIZE, stdin)
// ^^^^^
It doesn't make sense that you would read from a pipe and then immediately write the same thing back to the pipe. Also, if you never read from stdin, feof(stdin) will never be true.
Also, with fgets just check for a null result and then outside the loop, do the check for eof:
while (fgets(...) != NULL)
{
...
}
if (!feof(stdin))
{
// error handling
}
mkfifo() just creates special node in filesystem. And you are free to open it in any way. Actually there are two alternatives - POSIX "non-buffered" I/O: open()/write()/read() or standard buffered I/O: fopen()/fread()/fwrite(). First family operates on file descriptors while second one uses so called file streams: FILE. You can not mix these APIs freely. Just choose one and stick to it.
Standard I/O library offers some useful extra capabilities comparing to low-level non-buffered I/O. Like fgets() that you're trying to use. In this situation would be reasonable to use standard streams and replace open() with:
FILE* stream = fopen(FIFO_PATH, "r+");
Thus program will use FILE* instead of plain file descriptors. Also write() need to be changed to fwrite() immediately followed by fflush() to guarantee that written data are passed to FIFO.
P.S. In case of necessity it is possible to "wrap" low-level descriptors returned by open()(or something other) with standard FILE*. See fdopen(). But it is much like a workaround to use standard I/O API with special file objects that can not be opened with fopen().
I am trying to pass data from my perl script to my c program using a pipe (uni-directional).
I need to find a way to to do this without messing with the child programs STDIN or STDOUT, so I try creating a new handle and passing the fd.
I create 2 IO::Handles and create a pipe. I write to one end of the pipe and attempt to pass the File descriptor of the other end of the pipe to my child program that is being execed. I pass the file descriptor by setting an ENV variable. Why does this not work? (It does not print out 'hello world'). As far as I know, file descriptors and pipes are inherited by the child when exec'd.
Perl script:
#!/opt/local/bin/perl
use IO::Pipe;
use IO::Handle;
my $reader = IO::Handle->new();
my $writer = IO::Handle->new();
$reader->autoflush(1);
$writer->autoflush(1);
my $pipe = IO::Pipe->new($reader, $writer);
print $writer "hello world";
my $fh = $reader->fileno;
$ENV{'MY_FD'} = $fh;
exec('./child') or print "error opening app\n";
# No more code after this since exec replaces the current process
C Program, app.c (Compiled with gcc app.c -o child):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char ** argv) {
int fd = atoi(getenv("MY_FD"));
char buf[12];
read(fd, buf, 11);
buf[11] = '\0';
printf("fd: %d\n", fd);
printf("message: %s\n", buf);
}
Output:
fd: 3
message:
The message is never passed through the pipe to the C program. Any suggestions?
Your pipe file descriptors are set FD_CLOEXEC, and so are closed upon exec().
Perl's $^F controls this behavior. Try something like this, before you call IO::Pipe->new:
$^F = 10; # Assumes we don't already have a zillion FDs open
Alternatively, you can with Fcntl clear the FD_CLOEXEC flag yourself after creating the pipe.
I found the solution. Some people said that it was not possible with exec, that it would not see pipes or file descriptors, but that was not correct.
Turns out that perl closes/invalidates all fd > 2 automatically unless you say otherwise.
Adding the following flags to the FD fixes this problem (where READ is the handle here, NOT STDIN):
my $flags = fcntl(READ, F_GETFD, 0);
fcntl(READ, F_SETFD, $flags & ~FD_CLOEXEC);
Your program is failing because exec calls another program and never returns. It isn't designed for communication with another process at all.
You probably wrote the above code based on the IO::Pipe documentation, which says "ARGS are passed to exec". That isn't what it means, though. IO::Pipe is for communication between two processes within your Perl script, which are created by fork. They mean the execution of the new process, rather than a call to exec in your own code.
Edit: for one-directional communication, all you need is open with a pipe:
open my $prog, '|-', './child' or die "can't run program: $!";
print {$prog} "Hello, world!";
Rodrigo, I can tell you that your file descriptor is no longer valid when you exec into the c app.
Please be aware that I just say it is INVALID, but it still exists in the environment variables. The FD=3 will continue existing until the whole process ends.
You can check the fd by fcntl. The code is listing below
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char ** argv) {
int fd = atoi(getenv("MY_FD"));
char buf[12];
read(fd, buf, 11);
buf[11] = '\0';
printf("fd: %d, if fd still valid: %d\n", fd, fcntl(fd, F_GETFD));
printf("strlen %d\n", (int)strlen(buf));
printf("message: %s\n", buf);
}
You can see that MY_FD=3 will always in ENV as the process doesn't destroy itself, so you can get fd as 3. But, this file descriptor has been invalid. so the result of fcntl(fd, F_GETFD) will be -1, and the length you read from fd will be 0.
That's why you will never see the "hello world" sentence.
One more thing, #dan1111 is right, but you don't need to open a new pipe, as you have already done so.
All you need to is just set MY_FD=0, like
$ENV{'MY_FD'} = 0;
The STDIN/OUT is another independent process that always exists, so the pipe will not broken down when your perl app exec into c app. That's why you can read from what you input in app.
If your requirement is writing from another file hanle, please try to make that file handle an independent process and always exist, just like STDIN.
Maybe I'm just missing it, but isn't there a function equivalent to fprintf for file descriptors, or even a way to temporarily flip-flop between them?
You could look into dprintf (GNU extensions, not in C or POSIX) :
The functions dprintf() and vdprintf()
(as found in the glibc2 library) are
exact analogues of fprintf() and
vfprintf(), except that they output to
a file descriptor fd instead of to a
given stream.
EDIT As pointed by several of you in the comments, POSIX 2008 standardized these functions.
There is no C or POSIX (edit: prior to 2008) standard function to do printf on a file descriptor, but you can “open” a file descriptor as a FILE * with the POSIX-standard fdopen(int desc, const char *mode). I'm not sure how well supported flipping back to using the descriptor directly is, but I'm guessing it might work if you flush the buffer first…
Of course you could just implement your own using something like vsprintf, but obviously you then have to take care of the buffering.
For what it's worth, since dprintf is not a POSIX function, one could use the following if portability is an issue:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
int
fdprintf ( int fd, size_t bufmax, const char * fmt, ... )
{
char * buffer;
int n;
va_list ap;
buffer = ( char * ) malloc ( bufmax );
if ( !buffer )
return 0;
va_start ( ap, fmt );
n = vsnprintf ( buffer, bufmax, fmt, ap );
va_end ( ap );
write ( fd, buffer, n );
free ( buffer );
return n;
}
Most likely would want to check the return value of write, but you get the general idea. Obviously, this does not buffer like the FILE * routines do; I was looking more for the format specifiers and the ability to build the character data that would be written to the file descriptor, rather than worrying about buffering the data as it is being written.
No, there isn't as standard, but the two do different things. fprinft, as part of stdio, does things like buffer reads and writes, supports ungetc etc. Using a fd bypasses all that and calls the OS directly.
So they're not interchangeable. Flip flopping between them would screw up stdio buffering if nothing else
You can open the file descriptor as a normal file that can be handled by fprintf() with fdopen.