How can I determine if a file my code is reading or writing is mounted on a path on top of Lustre, GPFS, or NFS at runtime, from within a C/C++ code?
Edit: Working code:
#include <sys/vfs.h>
#include <iostream>
int main(int argc, char** argv) {
struct statfs sf;
statfs(argv[0], &sf);
std::cout << "f_type =" << std::hex << sf.f_type << "\n";
}
Sry about the half-C, half-C++.
You can use the statfs() system call and look at the f_type field. For Lustre the LL_SUPER_MAGIC is 0x0BD00BD0 (Object Based Disk). The NFS_SUPER_MAGIC is listed in the statfs(2) man page as 0x6969, no comment on what that might stand for. :-)
Related
I am learning C for an operating systems course and have just finished writing this program as per the textbook instructions:
#include <dirent.h>
#include <stdio.h>
#include "quit.h"
int main(int argc, char **argv) {
DIR *dir;
struct dirent *direntry;
arg_check(2, argc, "Specify a directory\n", 1);
if ( (dir = opendir(argv[1])) == NULL)
quit("opendir", 1);
while ((direntry = readdir(dir)) != NULL)
printf("%10d %s\n", direntry->d_ino, direntry->d_name);
closedir(dir);
exit(0);
}
This code is exactly copied from the textbook, but quit.h appears to be causing the compile to file. I have tried switching "quit.h" to , and "quit", but none of these have worked, and I cannot find other questions about this specific issue.
include "quit.h"
The word #include (with its hash # prefix) means that a file has to be read in; the content of that file (quit.h in this case) is processed exactly as if it was typed inside the program. Suppose you have a file name "test.h" which contains the single line
// this is a test
if you a have a program like this:
#include "test.h"
int main(int argc, char **argv)
...
the compiler processes (sees) exactly these lines:
// this is a test
int main(int argc, char **argv)
...
The file name specified after #include can be enclosed with angles or quotes. Conventionally, if angles are used, like
#include <stdio.h>
this means that the file (stdio.h in this case) is some system or standard file or so - in other simple words, someone else has written that file for you. If quotes are used, instead, the file specified is considered somehow part of the program you are compiling. Your quit.h seems like this. So you must have a file named quit.h. If you change that "quit.h" to "quit", a file named quit must be present in the same directory of the file you are compiling. (Actually things are more complicated, but don't mind it for now). Read your book, somewhere it should explain what is that file "quit.h".
I have a lot of unused macros in my code.
So, I am wondering.. If a macro is unused, does it takes up memory space in your program?
The type of macros I have are just the basic ones.
Example:
#define TEST_ID 0
Macros will be expanded during preprocessing phase so they don't exist in your program. They just take some space in your source code.
Edit:
In response to Barmar's comment, I did some research.
MSVC 2012: In debug build (when all optimizations are disabled, /Od), adding lines of macros won't cause the growth of the size of your program.
GCC: does provide a way to include macro in debugging information as long as you compile your program with a specific flag. See here. (I didn't know that before myself. Thank you, #Barmar, #Sydius)
No, doesn't takes space until is used, for this two pieces of code:
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("%d %s\n", argc, argv[0]);
return 0;
}
and
#include <stdio.h>
#define TEST_ID 0
int main(int argc, char *argv[])
{
printf("%d %s\n", argc, argv[0]);
return 0;
}
The ASM generated with gcc -S is the same.
macro is replaced by preprocessor before compilng start.
if you define a macro, and doesn't use it, the compiler will never see it.
As a preporatory task for a computer lab in school, we are asked to write a c program that simulates the login process in UNIX. The program should read username and password from a terminal, compare it to hashed values in a local file that is supposed to resemble /etc/passwd.
Here's what I've got:
/*
* Program mylogin.c
*
* This program prompts the user for a login name and password
*
*/
#define _XOPEN_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pwd.h>
#include <sys/types.h>
#include <string.h>
/* define some error constants */
#define NOUSER -1
/* define max size of a username */
#define USERNAME_SIZE 32
#define PASSWORD_SIZE 32
#define HASH_SIZE 32
#define FAILED_LIMIT 5
#define AGE_LIMIT 10
int read_username(char *username)
{
printf("login: ");
fgets(username,USERNAME_SIZE,stdin);
/* remove the CR included by getline() */
username[strlen(username)-1]='\0';
return(0);
}
int read_password(char *password)
{
printf("password: ");
fgets(password,PASSWORD_SIZE,stdin);
//getpass(password);
/* remove the CR included by getline() */
password[strlen(password)-1]='\0';
return(0);
}
int user_exists(const char *username)
{
struct pwdb_passwd *pw_entry;
pw_entry=getpwnam(username);
return((pw_entry)!=NULL);
}
int main(int argc,char **argv)
{
char username[USERNAME_SIZE];
char* password;
/* write "login:" and read user input */
read_username(username);
read_password(password);
if (!user_exists(username))
{
printf("Unknown user or authentication\n");
main(argc, argv);
}
struct pwdb_passwd *pw_entry = getpwnam(username);
char* hashed_password = crypt(password,pw_entry->pw_passwd);
if(strcmp(hashed_password, pw_entry->pw_passwd)==0)
{
if((pw_entry->pw_failed)<FAILED_LIMIT)
{
printf("User authenticated successfully\n");
pw_entry->pw_age++;
pw_entry->pw_failed = 0;
pwdb_update_user(pw_entry);
}else{
printf("User account locked\n");
main(argc, argv);
}
}
else
{
printf("Unknown user or authentication\n");
pw_entry->pw_failed++;
if(pw_entry->pw_failed>5){
printf("Too many failed attempts. Username now locked\n");
}
pwdb_update_user(pw_entry);
main(argc, argv);
}
return(0);
}
The struct pwdb_passwd is defined in the files pwdb_lib.c and pwdb_lib.h, which are already written.
When I compile the program, I get a couple of errors. For example on line 73, I get: "error: dereferencing pointer to incomplete type"
I don't understand why. It doesn't seem to like pw_entry->pw_passwd and things like that. More to the point, I get different errors when compiling under Windows with Code::Blocks (using gcc) than under Ubuntu with gcc. I find this pretty strange. I suspect it could be because I import pwd.h and that it only exists on Linux and not Windows. Could this be right? I tried creating my own pwd.h file and save it in the same directory, but it still didn't work. Moving to an ubuntu computer, I dont get errors from the pwd.h thing, but instead get errors on: "dereferencing pointer to incomplete type"
What's wrong with my code?
I also suspect memory leak in the user_exists function, but I'm not sure if it affects the overall program.
Even though pwdb_lib.c is already written, you need to include it in your source file.
Add
#include "pwdb_lib.h"
to your source and make sure you compile/link against pwdb_lib.c
By #includeing this file, you let your source file know about the definitions within it without providing it the implementation. At the end, when you compile your program with pwdb_lib.c (or link its object file, if that's what you're doing), you let any source which included these definitions know where they are implemented (and thus, give them the ability to use them).
If the header is named pwdb_lib.h, then why isn't your program doing an #include of it? It seems to include a different header (pwd.h), which is it?
The error you're getting is the one you'd expect if the declarations were missing.
Similar to how python has the convenient os.path.join() function, I was wondering if there was a good cross-platform way to do this in C.
My current approach is to set up some preprocessor directives with something like this
#ifdef defined(linux)
#define PATH_SEPARATOR "/"
#else
#define PATH_SEPARATOR "\\"
#endif
I'm pretty sure many cross-platform libraries have such functionality. Maybe you want to have a look at APR's apr_filepath_merge function.
In C++, you could use Boost:
#include <boost/filesystem.hpp>
using namespace boost::filesystem;
[...]
path path1("/tmp");
path path2("example");
path result = path1 / path2;
There is no standard way to do this. Do it yourself or use a library. For example the Apache Portable Runtime provides apr_filepath_merge.
For C, you could use cwalk which is a little cross-platform library to do file path related things (either cwk_path_join or cwk_path_join_multiple):
#include <cwalk.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char buffer[FILENAME_MAX];
cwk_path_join("hello/there", "../world", buffer, sizeof(buffer));
printf("The combined path is: %s", buffer);
return EXIT_SUCCESS;
}
Outputs:
The combined path is: hello/world
I have an application that has both two external kernel modules and a userspace daemon. I want to load the modules from the daemon code, written in C, at startup, and unload them on clean exit. Can I load them in a cleaner way than doing system("modprobe module"); and unload them using the corresponding rmmod?
init_module / remove_module minimal runnable example
Tested on a QEMU + Buildroot VM and Ubuntu 16.04 host with this simple parameter printer module .
We use the init_module / finit_module and remove_module Linux system calls.
The Linux kernel offers two system calls for module insertion:
init_module
finit_module
and:
man init_module
documents that:
The finit_module() system call is like init_module(), but reads the module to be loaded from the file descriptor fd. It is useful when the authenticity of a kernel module can be determined from its location in the filesystem; in cases where that is possible, the overhead of using cryptographically signed modules to determine the authenticity of a module can be avoided. The param_values argument is as for init_module().
finit is newer and was added only in v3.8. More rationale: https://lwn.net/Articles/519010/
glibc does not seem to provide a C wrapper for them, so we just create our own with syscall.
insmod.c
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#define init_module(module_image, len, param_values) syscall(__NR_init_module, module_image, len, param_values)
#define finit_module(fd, param_values, flags) syscall(__NR_finit_module, fd, param_values, flags)
int main(int argc, char **argv) {
const char *params;
int fd, use_finit;
size_t image_size;
struct stat st;
void *image;
/* CLI handling. */
if (argc < 2) {
puts("Usage ./prog mymodule.ko [args="" [use_finit=0]");
return EXIT_FAILURE;
}
if (argc < 3) {
params = "";
} else {
params = argv[2];
}
if (argc < 4) {
use_finit = 0;
} else {
use_finit = (argv[3][0] != '0');
}
/* Action. */
fd = open(argv[1], O_RDONLY);
if (use_finit) {
puts("finit");
if (finit_module(fd, params, 0) != 0) {
perror("finit_module");
return EXIT_FAILURE;
}
close(fd);
} else {
puts("init");
fstat(fd, &st);
image_size = st.st_size;
image = malloc(image_size);
read(fd, image, image_size);
close(fd);
if (init_module(image, image_size, params) != 0) {
perror("init_module");
return EXIT_FAILURE;
}
free(image);
}
return EXIT_SUCCESS;
}
GitHub upstream.
rmmod.c
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#define delete_module(name, flags) syscall(__NR_delete_module, name, flags)
int main(int argc, char **argv) {
if (argc != 2) {
puts("Usage ./prog mymodule");
return EXIT_FAILURE;
}
if (delete_module(argv[1], O_NONBLOCK) != 0) {
perror("delete_module");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
GitHub upstream.
Busybox source interpretation
Busybox provides insmod, and since it is designed for minimalism, we can try to deduce how it is done from there.
On version 1.24.2, the entry point is at modutils/insmod.c function insmod_main.
The IF_FEATURE_2_4_MODULES is optional support for older Linux kernel 2.4 modules, so we can just ignore it for now.
That just forwards to modutils.c function bb_init_module.
bb_init_module attempts two things:
mmap the file to memory through try_to_mmap_module.
This always sets image_size to the size of the .ko file as a side effect.
if that fails, malloc the file to memory with xmalloc_open_zipped_read_close.
This function optionally unzips the file first if it is a zip, and just mallocs it otherwise.
I don't understand why this zipping business is done, since we can't even rely on it because the try_to_mmap_module does not seem to unzip things.
Finally comes the call:
init_module(image, image_size, options);
where image is the executable that was put into memory, and options are just "" if we call insmod file.elf without further arguments.
init_module is provided above by:
#ifdef __UCLIBC__
extern int init_module(void *module, unsigned long len, const char *options);
extern int delete_module(const char *module, unsigned int flags);
#else
# include <sys/syscall.h>
# define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts)
# define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags)
#endif
ulibc is an embedded libc implementation, and it seems to provide init_module.
If it is not present, I think glibc is assumed, but as man init_module says:
The init_module() system call is not supported by glibc. No declaration is provided in glibc headers, but, through a quirk of history, glibc does export an ABI for
this system call. Therefore, in order to employ this system call, it is sufficient to manually declare the interface in your code; alternatively, you can invoke
the system call using syscall(2).
BusyBox wisely follows that advice and uses syscall, which glibc provides, and which offers a C API for system calls.
insmod/rmmod use the functions init_module and delete_module to do this, which also have a man-page available. They both declare the functions as extern instead of including a header, but the man-page says they should be in <linux/module.h>.
I'd recommend against the use of system() in any daemon code that runs with root permissions as it's relatively easy to exploit from a security standpoint. modprobe and rmmod are, indeed, the right tools for the job. However, it'd be a bit cleaner and much more secure to use an explicit fork() + exec() to invoke them.
I'm not sure there's a cleaner way than system.
But for sure, if you want to load/unload the modules from your userspace daemon, then you force yourself to run the daemon as root*, which may not be considered as secure.
*: or you can add the explicit commands in the sudoers file, but this will be a nightmare to manage when deploying your application.
You can perform the same tasks that modprobe and Co. do, but I doubt that could be characterized as cleaner.