How to get command line arguments inside LD_PRELOAD library - c

I would like to get argv from an LD_PRELOAD library, for instance, let us assume we call
LD_PRELOAD=/path/to/my/fopen ./program input
Inside my custom fopen I would like to get "input", hence the argv[1] of my program (also the argv[2] and so on).
Is it possible? How?

Read entire /proc/self/cmdline file. Command line arguments are separated with \0.
See man proc for full details.
Linux example without C standard library or error handling:
#include <unistd.h>
#include <fcntl.h>
#include <linux/limits.h>
int main() {
ssize_t i, n;
char cmdline[ARG_MAX];
int cmdline_fd = open("/proc/self/cmdline", O_RDONLY);
n = read(cmdline_fd, cmdline, sizeof cmdline);
for(i = 0; i < n; ++i)
if(!cmdline[i])
cmdline[i] = ' ';
cmdline[n - 1] = '\n';
write(STDOUT_FILENO, cmdline, n);
return 0;
}
Python version for reference:
cmdline = open('/proc/self/cmdline', 'r').read().replace('\0', ' ').strip()
print(cmdline)

Related

Passing arguments to execv() system call

I'm trying to build a version of a linux shell wherein, I take in the commands from the user and execute the commands using execv() system call.
I'm using strtok_r to tokenize the commands and pass the arguments to execv(). For example, when the user types in the command "ls -l", I first remove the leading/trailing white spaces and then tokenize the command into two tokens 1) ls, 2) -l. I concatenate the first token with a char path[10] in which I've already copied "/bin/" path. So, after concatenation, path becomes "/bin/ls". However, the execv() call fails. But, when I directly pass "/bin/ls" (instead of passing it as a variable) as the argument in execv(), it works. Code is shown below:
Please let me know where I've committed the mistake.
`
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
void removeTrailingLeadingSpaces(char* stringInput);
int main ()
{
printf("dash> ");
char* input;
size_t characters;
size_t bufsize = 32;
input = (char *)malloc(bufsize*sizeof(char));
characters = getline(&input,&bufsize,stdin);
removeTrailingLeadingSpaces(input);
char *args[2];
char* last;
char* token;
token = strtok_r(input," ",&last);
printf("%s\n", token);
removeTrailingLeadingSpaces(token);
char path[10];
strcpy(path,"/bin/");
strcat(path,token);
args[0] = path;
printf("%s\n",args[0]);
args[1] = strtok_r(NULL," ",&last);
args[2] = NULL;
int i = access(args[0],X_OK);
printf("result is %d",i);
int j= execv(args[0],args);
printf("result2 is %d",j);
return 0;
}
void removeTrailingLeadingSpaces(char* input)
{
while(isspace((unsigned char)*input))
input++;
int i=strlen(input)-1;
while(i>0)
{
if(input[i] == ' '|| input[i] == '\n'|| input[i] =='\t')
i--;
else
break;
}
input[i+1] = '\0';
}
`

How to check if command line parameters are integers

First of all, I am talking about old-fashioned ANSI-C (I mean the ANSI standard and no C99 or newer) compiled with gcc. I am only allowed to use the libraries that can be seen below in the code.
My problem is I have a program that is called in the following way on the Terminal:
program < integer_1 integer_2
While I have been able to figure out how to check for the number of arguments, I'm stuck on checking if those are integers.
If the program is called like this:
program < 1 -13
it should run without complaining but if it is run like this:
program < s 7
it should throw out an error.
Whatever I have tried so far has been utter rubbish. The best thing I have managed so far has been an error message if the second number has been a character. None of my tries has been able to deal with more than one digit but I have figured out why that is.
The problem is that I haven't used command line / terminal arguments with any programming language i now (C++, Java). I would really appreciate it if someone could show me how check for correct input as frankly I am out of ideas.
Am I correct that if I want to deal with numbers bigger than 9, I have to iterate through argv starting from index 2 until I find a space?
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int main(int arc, char *argv[])
{
if(arc != 3)
{
printf("Error: You have entered %d arguments, but two were expected!", arc - 1);
return -1;
}
return 0;
}
The easiest way out is to iterate over the argv[n]s and pass one by one to them to strtol() or similar. Then, check for the error and make the decision. To quote the man page, (emphasis mine)
long int strtol(const char *nptr, char **endptr, int base);
[...]
If endptr is not NULL, strtol() stores the address of the first invalid character in *endptr. If there were no digits at all, strtol() stores the original value of nptr in *endptr (and returns 0). In particular, if *nptr is not '\0' but **endptr is '\0' on return, the entire string is valid.
That said, program < integer_1 integer_2 is not exactly the way to pass the command-line arguments. If you want to pass the values arguments as command-line arguments, you shall lose the redirection operator and work with argc and argv[n]s directly..
Best way is to create a function for checking whether it is number or not.if the below function returns true then use atoi(argv[]) to convert them to integers to use it further.
bool isNumber(char number[])
{
int i = 0;
//checking for negative numbers
if (number[0] == '-')
i = 1;
for (; number[i] != 0; i++)
{
//if (number[i] > '9' || number[i] < '0')
if (!isdigit(number[i]))
return false;
}
return true;
}
Just a comment: not an answer
If you are going to use
program < arg1 arg2
You will not see arg1 or arg2 in the main parameters. arg1 is typically a filename or device which contain data which will be read by the program. I don't know if the program will even be able to access arg2. If you wish to pick up arg1 arg2 etc, lose the <
program arg1 arg2
You can try something like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int check_cmd_args(char const *str[], int numargs);
int
main(int argc, char const *argv[]) {
if (argc < 2) {
printf("Not enough command line arguements entered\n");
exit(EXIT_FAILURE);
}
if (check_cmd_args(argv, argc)) {
printf("All Command line arguements are integers\n");
} else {
printf("Error, non-integer command line arguement entered\n");
}
return 0;
}
int
check_cmd_args(char const *str[], int numargs) {
int n, i = 0;
for (n = 1; n < numargs; n++) {
if (str[n][0] == '-') {
i = 1;
}
for (; str[n][i]; i++) {
if (!isdigit(str[n][i])) {
return 0;
}
}
}
return 1;
}

C - Function that executes a command

How does one create a function that takes another command as an argument and executes that command. For example say I wanted to do
./func1 cat /etc/motd
and have it execute the cat command on the file /etc/motd so that it would print the message of the day. If someone could show me that would be much appreciated!
EDIT: I cannot use the system() call as later on I have to program a basic command shell. I just need to know how to execute commands so that when a user types in cat foo.txt it executes the command and displays the file. I guess what I'm trying to say is, how do you use execve()? What arguments go inside it?
Use you can use the system function.
Example:
system("cat foo.txt");
Will run this:
cat foo.txt
You could do something like that:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
size_t command_length = 0;
if(argc < 2)
return 0;
for(int i = 1; i < argc; i++)
command_length += strlen(argv[i]);
command_length += argc - 2; // spaces between words
command_length++; // terminator '\0'
char command[command_length];
memset(command, 0, sizeof(command));
for(int i = 1; i < argc; i++) {
strcat(command, argv[i]);
if(i < argc - 1)
command[strlen(command)] = ' ';
}
system(command);
}
It first determines the length of all command line parameters. After that it concatenates all command line parameters and inserts a space between each. Last but not least it calls the system() function with this string.
You need to use a C11 compiler with VLA support.
Here is a version without system():
#include <string.h>
#include <unistd.h>
#define MAX 1024
int main(int argc, char *argv[]) {
char buf[MAX] = "/usr/bin/";
size_t len = MAX - strlen(buf) + 1;
if(argc < 2)
return 0;
strncat(buf, argv[1], len);
execve(buf, argv + 1, NULL);
return 0;
}
This program only works under Linux. Unfortunately the execve() expects an absolute path. I assumed that the executable is located under /usr/bin. Additional work is necessary if that's not the case. For example you would have to examine the $PATH environment variable.

Using pointers instead of 2-D array string in C

I have a problem like here:
Write a program to read a multiple line text file and write the 'N' longest lines to stdout. Where the file to be read is specified on the command line.
Now I wrote my program like this:
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
int a,k,n,i=0,j;
int part;
char ar[1000][1000],str[1000];
/* char file[200]; */
/* scanf("%s",file); */
FILE *f = fopen(argv[1],"r");
if ( f == NULL || argc < 2)
{
return 0;
}
fscanf(f,"%d",&a);
while (fscanf(f,"%s",str)==1)
{
strcpy(ar[i++],str);
for ( k = 0 ; k < i ; k++ )
{
for ( j = k ; j < i ; j++)
{
if ( strlen(ar[k]) < strlen(ar[j]))
{
strcpy(str,ar[k]);
strcpy(ar[k],ar[j]);
strcpy(ar[j],str);
}
}
}
}
for ( j = 0 ; j < a ; j++ )
{
puts(ar[j]);
}
return 0;
}
First of all it is working well for me but on submission it is giving me runtime error.
Secondly I want to do it using pointers and dynamic allocation of memory. How can I do that?
Sorry, I went to bed for some time. Can you explain me what's wrong with my code. Why it is not working. I think no one explained me where I am doing wrong. Please let me know how can I draw attention of people after a few hours from posting my question. Again thanks a lot for all of you for giving me so much time.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
int a, n=0, i;
char **ar, str[1000];
int *lens;
FILE *f = fopen(argv[1],"r");
if ( f == NULL || argc < 2){
return 0;
}
fscanf(f,"%d",&a);
if(NULL==(ar=calloc(a, sizeof(char*))) || NULL==(lens=calloc(a, sizeof(int)))){
perror("malloc");
return -1;
}
while (fscanf(f, " %999[^\n]", str)==1){
++n;
int len = strlen(str);
for(i = 0;i < a;++i){
if(lens[i] < len){
free(ar[a-1]);
memmove(&lens[i+1], &lens[i], (a-i-1)*sizeof(int));
memmove(&ar[i+1], &ar[i], (a-i-1)*sizeof(char*));
ar[i]=strdup(str);
lens[i]=len;
break;
}
}
}
fclose(f);
for (i = 0 ; i < n && i < a ; i++ ){
puts(ar[i]);
free(ar[i]);
}
free(ar);free(lens);
return 0;
}
If you really want to conserve memory then you should not copy any contents of the file, but instead mmap it into your process and only save the offsets into the longest lines in your code. On longer files this should give you significantly more performance since it outsources memory management to the kernel.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
int main(int argc, char ** argv) {
char * filename = argv[1];
if (!filename) {
fprintf(stderr, "Usage: %s filename\n", argv[0]);
exit(-1);
}
int fd = open(filename, O_RDONLY);
if (fd < 0) {
perror("open");
abort();
}
struct stat file_stats;
if(fstat(fd, &file_stats)) {
perror("fstat");
abort();
}
char * file_contents = mmap(NULL // anywhere
, file_stats.st_size + 1 // file length + 1 byte for null terminator
, PROT_READ // we only need read only
, MAP_PRIVATE // this doesn't really matter since we are read only
, fd // from this file descriptor
, 0); // from beginning
if (file_contents == MAP_FAILED) {
perror("mmap");
abort();
}
// optional
// Expect page references in sequential order.
// Hence, pages can be aggressively read ahead, and may be freed soon after they are accessed.
madvise(file_contents, file_stats.st_size + 1, MADV_SEQUENTIAL);
struct {
size_t start;
size_t end;
} longest_lines[10]; // struct to hold ofsets for longest lines
memset(longest_lines, 0, sizeof(longest_lines)); // initialise
int shortest_line_id = 0;
char * start = file_contents;
char * next;
// while we are not at the end
while (start < file_contents + file_stats.st_size) {
if (!(next = strchr(start, '\n'))) // if line ternimator wasn't found, then go to the end
next = file_contents + file_stats.st_size;
size_t line_length = next - start;
// if this line is longer then our shortest
if (line_length > longest_lines[shortest_line_id].end - longest_lines[shortest_line_id].start) {
longest_lines[shortest_line_id].start = start - file_contents;
longest_lines[shortest_line_id].end = next - file_contents;
// determine new shortest line
int i;
for (i = 0; i < sizeof(longest_lines)/sizeof(*longest_lines); i++) {
if (
longest_lines[i].end - longest_lines[i].start
<
longest_lines[shortest_line_id].end - longest_lines[shortest_line_id].start
)
shortest_line_id = i;
}
}
// next line starts at this offset
start = next + 1;
}
int i; // print them
for (i = 0; i < sizeof(longest_lines)/sizeof(*longest_lines); i++) {
printf("%.*s\n", (int)(longest_lines[i].end - longest_lines[i].start), file_contents + longest_lines[i].start);
}
return 0;
}
Because it seems like some kind of homework or sth. I won't provide a full solution. But I will try to give you some peaces that you can start with.
You should divide your task by its responsibilities and try to create your programm a little more modular.
The several tasks seem to be:
Get the count of lines in your file
You can use a loop with fgets and a counter-variable to do this
Allocate a char*-Array (char**)
You can use malloc() with the counter-variable
Read all lines of your file into the allocated memory-sections and allocate memory for each line
You can use getline() to get a pointer to an allocated memory section
Really getline takes a lot work from you, but you can also combine the following functions to to the same:
fgets
malloc
realloc
strlen
Sort your char** by the length of your line
You can use strlen and some swap-function to archieve this (by simple apply sth. like bubblesort)
Output the first N lines of the sortet char** with printf
Don't forget to free all allocated memory with free()!
For Hints see the following post.
Note that the steps I provide do not optimize for memory or cpu-usage. But if your program works and you understoud what you've done this could be the next step.
Also this task shows the great benefits of modern programming languages where such a task would be a five line script. Here's the F# version that performs what you want to archieve:
open System
open System.IO
open System.Linq
[<EntryPoint>]
let main args =
try
let linesToPrint = Int32.Parse(args.ElementAt(0))
let path = #"C:\Users\XYZ\Desktop\test.txt"
File.ReadAllLines(path).OrderByDescending(fun line -> line.Length).Take(linesToPrint).ToArray()
|> Array.iter(printfn "%s")
with
| ex -> eprintfn "%s" ex.Message
0
Note the sortness and the readability of the solution. Nevertheless it is worth to program such a task in c or maybe also in assembler to get a deeper understanding for how computers work.
If you're happy sticking with your maximum line length of 1000 you can do:
char (*ar)[1000] = malloc(a * sizeof *ar);
if (!ar)
// error handling....
and then use ar as you used it before.
There is a problem : fscanf(f,"%s",str). This reads a single word, and does no length checking. I guess you actually want to read a whole line, and not overflow your buffer:
fgets(str, sizeof str, f);
If you want to you can remove the newline from str after this but actually it will make no difference to your program.
There is currently a problem with your algorithm; you read every line into ar. Instead you should just make ar's size be a (I presume that a is meant to be N), take out the line strcpy(ar[i++],str);, and only insert the line if it is bigger than the current smallest member.

How to search through $PATH

I have a task to write a c program that can search for a directory in all the directories listed in $PATH using fork and exec. My question is how do i get the paths from $PATH in a manner that i can then use it in my code with execl
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char* argv[]) {
int pid = 0;
for(int i = 0; i < argc; i++) {
if (pid = fork() != 0){
printf("Arg%d: %c\n", i, *argv[i]); // replace with exec ls -l <dir>/<arg>
return 0;
}
}
return 0;
}
You can get the PATH environment variable using getenv() (man 3 getenv). Copy the string into a char* and then split it with strtok() (man 3 strtok) using ':' as delimiter. You should copy the original string into a new char* because the pointer you get from getenv() actually points inside the environment and strtok() will modify the argument you pass to it. You can then fork in a loop for each substring.

Resources