I am using gcc 10.1.1 on Fedora 32.
The following program is to read from a file given by the user and print it into stdout. The code is an example in Modern C by Jens Gusdets.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
enum { buf_max = 32,};
int main(int argc, char* argv[argc +1]){
int ret = EXIT_FAILURE;
char buffer[buf_max] = {0};
for(int i = 0; i<argc ; ++i){
FILE* instream = fopen(argv[i] , "r");
if(instream){
while(fgets(buffer , buf_max , instream)){
fputs(buffer, stdout);
}
fclose(instream);
ret = EXIT_SUCCESS;
} else{
fprintf(stderr, "Could not open %s: ", argv[i]);
perror(0);
errno = 0;
}
}
return ret;
}
But when I run the program it is printing some gibberish like :
$ ./read some.txt
ELFDib64/ld-linux-x86-64.so.2Co.6locationderrGLIBC_2.2.5###/��^H��H���PTI��##H=X##�H��H�H��t�f.��z������UH��H��#�}�H�u��E����`..��������ATA��UH�-T+t1��u�H�[]A\A]A^A_�ff.������A�C
G#n8A0A(B BB���oGCC: (GNU) 10.1.1 20200507 (Red Hat 10.1.1-1)�#�#�#�#�#�#�#�#1�# gcc 10.1.1 2020050730k_clashectionYSSERTIONSGA!stack_realign�# gcc 10.1.1 2020050730k_cl#Z�##nux/10/../../../../lib64/crt1.oc_endbin_init.c_end.hott.c.unlikelyd.unlikelytupinit.c_end.exit_reloc.cc_endothotikelynd.unlikelyoc.c.startuploc.c_end.startupic_reloc.c.exit_reloc.c_end.exitrelocate_static_pie.startbin__dl_relocate_static_pie.endsed.0_array_entrye_dummy_init_array_entry__FRAME_END___DYNAMICNU_EH_FRAME_HDRTABLE_location##GLIBC_2.2.5LIBC_2.2.5_2.2.5c_start_main##GLIBC_2.2.5##GLIBC_2.2.5ntf##GLIBC_2.2.5libc_csu_initic_pieGLIBC_2.2.55nterpABI-tagtr.rela.dynh_framebsstesuh #�?#0yey my c program worked.
some.txt = yey my c program worked.
argv[0] is always the program name. You're printing the contents of your executable in addition to any extra files identified by the filenames passed in the arguments by starting your for loop at i = 0.
If you start at i = 1, it will exclude your executable.
Related
Is there a way to redirect output of a command line which returns integer as an output to a variable in C?
for example, if the command is "cmd", then is there a way to redirect its output (an integer) and store it in variable in C?
I tried using popen and fgets but it seems to be working only with characters. Any suggestions?
It works perfectly fine with popen and fgets:
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char *argv[])
{
const char *cmd = argc > 1 ? argv[1] : "echo 42";
char buf[32];
FILE *fp = popen(cmd, "r");
if( fp == NULL ){
perror("popen");
return 1;
}
if( fgets(buf, sizeof buf, fp) == buf ){
int v = strtol(buf, NULL, 10);
printf("read: %d\n", v);
}
return 0;
}
If you want to convert a character string from the standard input, you could use fgets and then use atoi to convert the input to an integer.
If you want to convert the output of a command, let's say ls and store the output of the command to a variable, you could learn about fork, dup2, pipe, and exec function family.
More about this topic on this tutorial : Capture the output of a child in C. This tutorial also provide an example with popen if you want to keep things "high level".
Here is an even simpler example using popen() and fscanf():
#include <errno.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
FILE *fp = popen("date '+%s'", "r");
long seconds;
if (fp == NULL) {
fprintf(stderr, "popen failed: %s\n", strerror(errno));
return 1;
}
if (fscanf(fp, "%ld", &seconds) == 1) {
printf("epoch seconds: %ld\n", seconds);
pclose(fp);
return 0;
} else {
fprintf(stderr, "invalid program output\n");
pclose(fp);
return 1;
}
}
we are trying to copy a binary/elf file into a shared-memory region of our system and then execute it thereafter. We don't want to call our "client"-program directly, since we need to execute it from the memory itself for our purpose.
While we know that our approach (described below) won't really work, we are (obviously) trying to get it to work. How would it be possible to copy a binary/elf/etc. file directly into the (shared)-memory and execute it thereafter? Maybe we just compiled it in the wrong way? Or something else was done wrong?
We also don't want to convert it into hex/shell-code, we already did that. We are looking for an easier and more practical solution.
Is anyone able to help? Would be much appreciated!
Two programs:
"Host"-Program (copy & execute client-program in shared memory)
"Client"-Program (basically a hello-world echo)
"Client"-Program:
#include <stdio.h>
int main()
{
printf("Hello, World!\n");
return 0;
}
Compiled with gcc -o binfile clientprogram.c -static.
"Host"-Program:
#include <string.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
FILE *fp; //filepointer
size_t size; //filesize
unsigned char *buffer; //buffer
fp = fopen("binfile","rb");
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);
buffer = (unsigned char *) malloc(size);
if (fp == NULL){ //file empty?
printf("Error: There was an Error reading the file %s \n", "binfile");
exit(1);
}
else if (fread(buffer, sizeof *buffer, size, fp) != size){
printf("Error: There was an Error reading the file %s\n", "binfile");
exit(1);
}else{
int i;
// for(i=0; i<size;i++){
// printf("%02x", buffer[i]);
// }
}
void *mem = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
memcpy(mem, buffer, size);
mprotect(mem, size, PROT_READ|PROT_WRITE|PROT_EXEC);
void (*func)();
func = (void (*)()) buffer;
func();
munmap(mem, size);
fclose(fp);
free(buffer);
return 0;
}
Compiled with gcc hostprogram.c.
Build the client as a PIE, with -rdynamic. Then you'll be able to dlopen() it and dlsym() its main symbol (dlopen() will do the mmaping and mprotecting for you, as you'll be able to see if you strace the program), after which you'll be able to run its main from within the address space of the host.
Example:
#!/bin/sh
cat > client.c <<EOF
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("Hello, World!: from %ld\n", (long)getpid());
return 0;
}
EOF
gcc -fpic -c client.c
gcc -pie -rdynamic -o client client.o
cat > host.c <<EOF
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
printf("Hello, I'm your host: %ld\n", (long)getpid()); ;
void *client_hndl;
typedef int main_t(int, char**);
main_t *client_main;
client_hndl = dlopen("./client", RTLD_LAZY);
if (!client_hndl){
fprintf(stderr, "%s\n", dlerror());
exit(1);
}
client_main = (main_t*)dlsym(client_hndl, "main");
if (!client_main){
fprintf(stderr, "%s\n", dlerror());
exit(2);
}
return client_main(1, (char*[]){"client", 0});
}
EOF
gcc host.c -ldl
./client
echo =============
./a.out
Example output:
Hello, World!: from 14520
=============
Hello, I'm your host: 14521
Hello, World!: from 14521
You are looking for a solution to this GLIBC feature request.
This feature request is 7 years old, and it's somewhat unlikely that anything will happen with it any time soon.
Your best bet is probably to do roughly what you are already doing (building a fully-static binary).
Your approach doesn't work because the executable you built requires to be loaded at the address it was linked at (visible in readelf -l binfile as the address of the first PT_LOAD segment. You would need to mmap your binfile there with MAP_FIXED, no other address will do.
You also need to read and decode the Elf{32,64}_Ehdr that is found at the beginning of the file to find entry point to jump to. You currently are jumping to the ELF header itself, but that header is not where the execution should start.
Let's say I have a file in Linux with this path:
/path/to/file/test.mp3
I want to know the path to its device. For example I want to get something like:
/dev/sdb1
How do I do this with the C programming language?
I know the terminal command to do it, but I need C functions that will do the job.
EDIT:
I have read this question before asking mine. It doesn't concretly mention code in C, it's more related to bash than to the C language.
Thanks.
You need to use stat on the file path, and get the device ID st_dev and match that to a device in /proc/partitions
Read this for how to interpret st_dev: https://web.archive.org/web/20171013194110/http://www.makelinux.net:80/ldd3/chp-3-sect-2
I just needed that inside a program I am writing...
So instead of running "df" and parsing the output, I wrote it from scratch.
Feel free to contribute!
To answer the question:
You first find the device inode using stat() then iterate and parse /proc/self/mountinfo to find the inode and get the device name.
/*
Get physical device from file or directory name.
By Zibri <zibri AT zibri DOT org>
https://github.com/Zibri/get_device
*/
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <libgen.h>
int get_device(char *name)
{
struct stat fs;
if (stat(name, &fs) < 0) {
fprintf(stderr, "%s: No such file or directory\n", name);
return -1;
}
FILE *f;
char sline[256];
char minmaj[128];
sprintf(minmaj, "%d:%d ", (int) fs.st_dev >> 8, (int) fs.st_dev & 0xff);
f = fopen("/proc/self/mountinfo", "r");
if (f == NULL) {
fprintf(stderr, "Failed to open /proc/self/mountinfo\n");
exit(-1);
}
while (fgets(sline, 256, f)) {
char *token;
char *where;
token = strtok(sline, "-");
where = strstr(token, minmaj);
if (where) {
token = strtok(NULL, " -:");
token = strtok(NULL, " -:");
printf("%s\n", token);
break;
}
}
fclose(f);
return -1;
}
int main(int argc, char **argv)
{
if (argc != 2) {
fprintf(stderr, "Usage:\n%s FILE OR DIRECTORY...\n", basename(argv[0]));
return -1;
}
get_device(argv[1]);
return 0;
}
output is just the device name.
Example:
$ gcc -O3 getdevice.c -o gd -Wall
$ ./gd .
/dev/sda4
$ ./gd /mnt/C
/dev/sda3
$ ./gd /mnt/D
/dev/sdb1
$
Use this command to print the partition path:
df -P <pathname> | awk 'END{print $1}'
I'm trying to write a C program, that make user able to write stuff in a file. My Problem is that after making and running the program the file stay empty ?? any idea how can I solve this.
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
// the user should give a file to write the file
int main (int argc , char**argv)
{
int fd; // file descriptor
char ret; // the character
int offset;
if(argc != 2) {
printf("You have to give the name or the path of the file to work with \n");
printf("Exiting the program \n")
return -1;
}
fd = open (argv[1], O_WRONLY/*write*/|O_CREAT/*create if not found */, S_IRUSR|S_IWUSR/*user can read and write*/);
if (fd == -1) {
printf("can'T open the file ");
return -1;
}
printf("At wich position you want to start ");
scanf("%d",&offset);
lseek(fd,offset,SEEK_SET);
while(1) {
ret = getchar();
if(ret == '1') {
printf("closing the file");
close (fd);
return 1;
}
else
write (fd,red, sizeof(char));
}
return 0;
}
thanks in advance for you help.
I have made some changes,this should work:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
int main (int argc , char**argv)
{
int fd; // file descriptor
char ret; // the character
int offset;
if(argc != 2){
printf("You have to give the name or the path of the file to work with \n");
printf("Exiting the program \n"); **//There was ';' missing here**
return -1;
}
fd = open (argv[1], O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR);
if (fd == -1) {
printf("can'T open the file ");
return -1;
}
printf("At wich position you want to start ");
scanf("%d",&offset);
lseek(fd,offset,SEEK_SET);
while(1){
ret = getchar();
if(ret == '1'){
printf("closing the file");
close (fd);
return 1;
}
else
write (fd,&ret, sizeof(char)); **//red has been changed to &ret**
}
return 0;
}
One error I can notice, the call of write function:
write (fd,red, sizeof(char));
should be:
write (fd, &red, sizeof(char));
You forgot & before red, write need address.
syntax of write: int write( int handle, void *buffer, int nbyte );
This will cause an undefined behavior in your code at run time
Edit: in write function you are using red that is not defined, I think it should be ret variable in your code. correct it as write (fd, &ret, sizeof(char));
second, you forgot ; after printf("Exiting the program \n") in if, but I also think its mistake while posting question as you says you are getting run time error.
side note: If you are using gcc compiler then you can use gcc -Wall -pedantic to generate warnings
It should be:
write (fd,&ret, sizeof(char));
write takes the pointer to the memory position, and since ret is a single char, you need to pass a pointer to it.
so I'm using the zlib package on Ubuntu. I'm trying to figure out how to use gzopen and gzread correctly, this is what I have so far
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <zlib.h>
#define NUM_BUFFERS 8
#define BUFFER_LENGTH 1024
char buf[BUFFER_LENGTH];
int main(int argc, const char* argv[])
{
int status;
gzFile file;
file = gzopen("beowulf.txt", "w");
int counter = 0; /*when the counter reachers BUFFERS_FULL, stop*/
if(file == NULL)
{
printf("COULD NOT OPEN FILE\n");
return 1;
}
while(counter < NUM_BUFFERS)
{
status = gzread(file, buf, BUFFER_LENGTH - 2);
printf("STATUS: %d\n", status);
buf[BUFFER_LENGTH - 1] = "\0";
printf("%s\n", buf);
counter++;
}
gzclose(file);
printf("STATUS: %d\n", status);
return 0;
}
The gzread("STATUS: %d\n",status); returns -2, and I have no clue why. Any help would be appreciated.
Mode "w" indicates that you are preparing to create a new archive:
file = gzopen("beowulf.txt", "w");
You've just truncated the file to zero length.
Also, you should use the binary mode flag: "wb" or "rb".
Also, it's a bit weird that your supposed .gz-archive has an extension .txt.
Read the docs, docs rule. :)
Log the error type using function gzerror(). Since it is -2, it won't be an end-of-file error. Possibly any of the following errors.
Z_DATA_ERROR
A CRC error occurred when reading data; the file is corrupt.
Z_STREAM_ERROR
The stream is invalid, or is in an invalid state.
Z_NEED_DICT
A dictionary is needed (see inflateSetDictionary()).
Z_MEM_ERROR
Insufficient memory available to decompress.
From http://refspecs.linuxbase.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/zlib-gzread-1.html :
If the return is positive it is the number of bytes read, you can use that to put the NULL terminator in the right place.
You can use gzerror if the return code is <0 to work out what the error is.