mount of SD card in C program - c

My environment: Petalinux on Xilinx/Zynq Soc
I am trying to mount microSD card.
I confirmed that following code works to mount SD under root login.
#include <stdio.h>
int main(void)
{
int ret;
ret = system("mkdir /media/card");
printf("%d\n", ret);
ret = system("mount /dev/mmcblk0p1 /media/card");
if (ret == 0) {
printf("sd mounted to /media/card\n");
} else {
printf("sd mount : fail\n");
}
}
Is this a normal way to mount SD card in linux C program?
Or are there any special systemcall/API used in C program to mount SD?

Mounting filesystems is done with the mount(2) system call. That's what the mount program (that you're calling via system) actually does.
The normal way to mount a filesystem from a C program is to use the system call. Forking off a separate process to run a shell command (i.e. using the system function) is inefficient and prone to bugs (such as shell-injection security vulnerabilities), and gives you less control. The same goes for calling the mkdir program; just use the mkdir(2) system call instead. (Your program above is essentially a shell script written in C, which is silly.)

Related

execve(2) system commands exit before execution

I am trying to implement a container, and for that I create a process using the clone(2) system call with the appropriate flags:
if ((child_pid = clone(child_main, process_struct.Stack + process_struct.StackPtr,
CLONE_NEWCGROUP
|CLONE_NEWIPC
|CLONE_NEWNET
|CLONE_NEWNS
|CLONE_NEWPID
|CLONE_NEWUTS
|SIGCHLD, &process_struct, checkpoint)) == -1){
fprintf(stderr,"Failed...%m \n");
exit(EXIT_FAILURE);
}else{
fprintf(stderr,"Done\n");
waitpid(child_pid, NULL, 0);
}
inside child_main() I Change the host name for the process's namespace, also i set the mount namespace, I installed a Linux file system hierarchy on a partition like a normal Linux installation (I did that to create a clean file system image clean of my files and binaries) and then I set the propagation type to MS_UNBINDABLE, then I pivot_root(2) to change my process's root directory.
const int child_main(struct process *process_struct, int *checkpoint){
char c;
fprintf(stderr,"=> IPC setup...");
//double check the IPC
close(checkpoint[1]);
fprintf(stderr,"Done\n");
if ( sethostname(process_struct->Hostname,
strlen(process_struct->Hostname)) || mounting(process_struct)){
return -1;
}
// startup the IPC pipes
read(checkpoint[0], &c, 1);
if(execve("/bin/bash", (char*)0, NULL) == -1 ){
fprintf(stderr,"--> Launching process Failed %m\n");
return -1;
}
return 0;
}
The problem is that my system goes over the execve(2) and does not launch the /bin/bash and the program flows without errors. When I add system(2) statement before the execve(2) : system("ls"); it lists the appropriate file system and current working directory. Also when I change the execve(2) paramters to either:
execve("/bin/ls", (char*)0, NULL) or execve("/bin/pstree", (char*)0, NULL) or any other parameter it will return an error of: No such file or directory or A NULL argv[0] was passed through an exec system call, also when I strace my program at the execve(2) system call it gives: NULL, 0, NULL) = 17992
The error has nothing to do with the file system image, I have performed more tests and are as the follwoing, I used for my mount namespaces my system's filesystem not the one I installed on a partition and running /bin/bash doesn't still work, I created a simple C program and compiled it, and it ran fine so there is something wrong that prevent bin/bash from being executed, to further test these results I reused for my mount namespaces the file system from my I moved the same executable to the file system first under "/" and second under the same path
my main system path to the executable= /home/omar/docs/test.out
my mounted file system from the partition path to the executable= /home/omar/docs/test.out
since I wanted to check if the same path might have caused a confusion while adding to each executable a statment so can tell which path did my program take, and it worked fine without any problem and correctly as expected, so the problem is just that system essential commands will not work.
You need to pass a proper argv array to execve. And if you just want to pass on the current environment, use execv rather than execve.
char *argv[] = {"bash", NULL};
if(execv("/bin/bash", argv) == -1 ){
perror("execv");
return -1;
}

How to robustly write to a slow & unreliable NFS

I'm not an expert in C and I'm looking for some advice to to make my program more robust and reliable. Just to give some context: I've written a program to do some scientific computation that takes quite a long time (about 20h) that I'm executing on a large university HPC linux cluster using a SLRUM scheduling system and NFS mounted file systems. What seems to happen is that some time during the 20h the connection to the file system goes stale (on the entire machine; independent of my program) and the first attempt to open & write a file takes a really long time and that results in a segfault cored dumped error that I have so far not been able to precisely track down. Below is a minimal file that at least conceptually reproduces the error: The program starts, opens a file and everything works. The program does some long computation (simulated by sleep()), tries to open & write to the same file again, and it fails. What are some conventions to make my code more robust and reliably write my results to file without crashing?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv) {
// Declare variables
FILE *outfile;
char outname[150] = "result.csv";
// Open file for writing
printf("CHECKING if output file '%s' is writable?", outname);
outfile=fopen(outname, "w");
if (outfile == NULL) {
perror("Failed: ");
exit(EXIT_FAILURE);
}
fclose(outfile);
printf(" PASSED.\n");
// Do some computation that takes really long (around 19h)
sleep(3);
// Open file again and Write results
printf("Writing results to %s ...", outname);
outfile=fopen(outname, "w");
if (outfile == NULL) {
perror("Failed writing in tabulate_vector_new: ");
exit(EXIT_FAILURE);
}
fprintf( outfile, "This is the important result.\n");
fclose(outfile);
printf(" DONE.\n");
return 0;
}
It seems odd that your program would segfault due to an NFS issue. I would expect it to hang indefinitely, not crash. That having been said, I would suggest forking a new process to check whether the NFS mount is working. That way, your important code won't be directly involved in testing the problematic file system. Something like the following approach may be useful:
pid_t pid = fork();
if (pid == -1)
{
// error, failed to fork(). should probably give up now. something is really wrong.
}
else if (pid > 0)
{
// if the child exits, it has successfully interacted with the NFS file system
wait(NULL);
// proceed with attempting to write important data
}
else
{
// we are the child; fork df in order to test the NFS file system
execlp("df", "df", "/mnt", (char *)NULL)
// the child has been replaced by df, which will try to statfs(2) /mnt for us
}
The general concept here is that we utilize the df command to check whether the NFS file system (which I assume is at /mnt) is working. If it's temporarily not working, df should hang until it starts working again, and then exit, returning control to your program. If you suspect df might hang forever, you could enhance my example by using alarm(2) to wait a certain period of time, probably at least a few minutes, after which you could retry running df. Note that this could result in zombie df processes sticking around.
In the end, the correct solution is to try to get a more reliable NFS server, but until you can do that, I hope this is helpful.

C program to detect USB drive in Linux

I have a embedded device running Linux Angstrom. I need to detect a USB drive. So when a USB drive is inserted, I need to automatically copy data from the USB to internal memory of the embedded device.
To detect the USB, I am using below code:
DIR* dir = opendir("/media/sda1/");
if (dir)
{
printf("USB detected\n");
//rest of the code
//to copy data from the USB
}
This works normally but sometimes after copying is done, I remove the USB but the name of the mount point (sda1) remains there. So after removing the USB, it again try to copy the data (because sda1 is there in media) and then shows error because physical no USB is connected. What is the best way to detect if USB is connected and if connected then after copying how to eject it properly. Here I cannot use udisks because its not available for the linux angstrom I am using for this embedded device. So only generic linux commands will work.
Any help. Thanks
One naive approach is the following:
execute mount | grep /dev/sda1
parse the output: if there is no output, that means that sda1 is not mounted
You may have to adapt the code to your specific platform.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
/* launch a command and gets its output */
FILE *f = popen("mount | grep /dev/sda1", "r");
if (NULL != f)
{
/* test if something has been outputed by
the command */
if (EOF == fgetc(f))
{
puts("/dev/sda1 is NOT mounted");
}
else
{
puts("/dev/sda1 is mounted");
}
/* close the command file */
pclose(f);
}
return 0;
}

umount doesn't work with device in C (but it works in Terminal)

I'm trying to unmount programmatically an USB drive (device /dev/sdb1). If I run in a Linux terminal sudo umount /dev/sdb1 it works. However, if I gcc compile and run the following C snippet as sudo, it errors with ERRNO 22 = EINVAL (Invalid argument).
This is the code:
#include "unistd.h"
#include "sys/mount.h"
#include "errno.h"
int main()
{
int r = umount2("/dev/sdb1", MNT_FORCE);
if (r != 0) return errno;
else return 0;
}
The same applies for umount(). MNT_FORCE doesn't change anything.
The function works if I pass the mount point instead of device, yet the documentation says it works with both. I find this way more reliable than reading /etc/mtab to get the mount point and use that.
Function: int umount2 (const char *file, int flags)
Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.
umount2 unmounts a filesystem.
You can identify the filesystem to unmount either by the device special file that contains the filesystem or by the mount point. The effect is the same. Specify either as the string file.
What is wrong?
Give your mount_path. It should work.
int r = umount2("/your_mount_path", MNT_FORCE);

linux application remote debugging without gdb

I wrote a linux program as below:
int g_para1 = 10;
int g_para2 = 11;
void SetPara(int para1, int para2)
{
g_para1 = para1;
g_para2 = para2;
}
void DumpPara()
{
printf("g_para1 = %d, g_para2 = %d\n", g_para1,g_para2);
}
void init()
{
int pid;
if(pid = fork())
exit(0);
else if(pid < 0)
exit(1);
setsid();
if(pid = fork())
exit(0);
else if(pid < 0)
exit(1);
return;
}
int main()
{
signal(SIGCHLD, SIG_IGN);
init();
while(1)
{
DumpPara();
sleep(10);
}
return 0;
}
And then compile and run it in the shell
gcc -o test test.c
./test
it will show the print "g_para1 = 10, g_para2 = 11" every 10 seconds.
My question is:
If I execute "SetPara 20, 30" in the shell, i want the print shows "g_para1 = 20, g_para2 = 30".
What should i do to make it work?
If I do nothing, it will show
SetPara 20,30
SetPara: command not found
First, an application (in C, compiled with debug info, e.g. with gcc -Wall -g) can be remotely debugged. If you have ssh available, you simply debug using gdb thru a terminal connection via ssh. Otherwise, read the documentation of GDB, it has a chapter on remote debugging.
Then, it looks like you wish to embed your application into a scripting language interpreter. Then try lua, or GNU guile, or perhaps even python or ocaml. Notice that embedding your application inside an interpreter (or symetrically, putting an interpreter inside your application) is a heavy architectural decision and has major impacts on its design.
BTW, some Unix shells can be extended thru plugins, e.g. zsh (with modules...). Plugins use dynamic loading facilities like dlopen(3) (you might consider using them in your application).
But an ordinary shell can only start external programs using fork(2) & execve(2)
BTW, you might consider making your application behave as a Web server by using some HTTP server library inside it, e.g. libonion. You could also consider remote procedure call techniques, e.g. JSON RPC
Read also Advanced Linux Programming
As for your improved question, just define a convention in main arguments: you could want that if you invoke your program with arguments --setpara 23 45 it will call SetPara(23,45) e.g. with code like:
int main (int argc, char**argv) {
if (argc>3 && !strcmp[argv[1], "--setpara")) {
int x = atoi(argv[2]);
int y = atoi(argv[3]);
SetPara (x,y);
}
but you should do serious arguments passing e.g. with getopt_long (and accept --help & --version). See this answer and that one.
If you want SetPara to be called elsewhere in your code, the arguments to main should trigger other behavior (e.g. initializing global data, initializing an embedded interpreter, etc.).
Notice that each process has its own address space in virtual memory. See also this answer.
PS: your question is very unclear; I tried to give various ways of approaching your issues, which I am mostly guessing. You should study -for inspiration at least- the source code of some existing free software related to your goals.

Resources