I wanted a function or a system call similar to mountpoint command Linux.
(this command will check if given directory is a mount point or not)
I'm not aware of an actual syscall that would do that, but what you can do is compare the device numbers of the directory that you want to check and its parent. That can be done with stat. Example code (error checking omitted) :
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(void) {
struct stat mp, mp_parent;
stat("/boot", &mp);
stat("/boot/..", &mp_parent);
if (mp.st_dev != mp_parent.st_dev)
printf("/boot is a mount point.\n");
else
printf("/boot is not a mount point.\n");
return 0;
}
The need for functions like this is usually (though not always) a red flag that you're not embracing the abstractions of the system...and you should reconsider whatever-it-is-you're-doing. If there is some choice you're making in your software based on whether something is a mountpoint...consider making that alternative behavior a parameter which can be controlled via scripting. People can parameterize your program to adopt that behavior via using the native mountpoint command, or whatever else.
With that disclaimer aside, here's an implementation of mountpoint.c:
https://fossies.org/linux/sysvinit/src/mountpoint.c
...and a reference on "testing the type of a file"
http://www.aquaphoenix.com/ref/gnu_c_library/libc_160.html
Write your own app to parse /proc/mounts. Then compare your path with paths from /proc/mounts. If they are equal then path is a mount point.
Related
For the problem described at bash - Detect if a script is being run via shebang or was specified as a command line argument - Unix & Linux Stack Exchange, we need to distinguish between cases when a script is run via shebang and as an argument to the interpreter.
An answer to that question suggests getting the pre-shebang executable name using getauxval(AT_EXECFN) -- which works, but only in Linux.
Since the Pyenv project also officially supports MacOS, we need an equivalent for that if we are to consider that solution.
I've checked Finding current executable's path without /proc/self/exe -- but both _dyld_get_image_name(0) and _NSGetExecutablePath give the post-shebang name. Here's a sample program that I used to do the checking (see the question link above on how it's used; its compilation result needs to be put in place of the python3 Bash script given in that question):
#include <stdio.h>
#include <unistd.h>
/*#include <sys/auxv.h>*/
#include <mach-o/dyld.h>
#include <sys/param.h>
#include <alloca.h>
int main(int argc, char** argv) {
//char *at_execfn = (char*)getauxval(AT_EXECFN);
//const char *at_execfn = _dyld_get_image_name(0);
char *at_execfn = (char*)alloca(MAXPATHLEN);
uint32_t at_execfn_len = MAXPATHLEN;
_NSGetExecutablePath(at_execfn,&at_execfn_len);
printf("original executable: '%s'\n",at_execfn);
for(int i=0; i<argc; i++) {
printf("'%s'\n",argv[i]);
}
execvp("python3",argv);
}
This answer is based on the following assumptions; I'm sure others will vet whether they are true, but to my understanding, they are:
Python scripts will only use the shebang if they are executed directly.
Otherwise, the first command line argument will always be python, python3, or some other variation (python3.x, etc.).
You can already get the path to the original file, which is good because you can read what the shebang says, but you don't yet know whether the shebang was used, right? Python 3.10 offers an appealing solution: sys.orig_argv, which includes all the command line arguments, not just those from the program name forward as you get with normal sys.argv.
However, I'm sure you won't be implementing a 3.10-exclusive feature into pyenv! If that is the case, you can see the older C-API Py_GetArgcArgv, whose docs simply state:
Get the original command line arguments, before Python modified them.
Either way, I think that having the file path so you can read the shebang is the first part of the puzzle. The second part is figuring out if the shebang was actually used, and I think that the answer is in the command line arguments for most cases.
I use snprintf to write formatted data to disk, but I have one problem, how do I save it to the user's home directory?
snprintf(filename, sizeof(filename), "%s_%s.sho", client, id);
The user controls his environment - so the HOME environment variable may not be correct or it may not even be set.
Use getuid() and getpwuid() to get the user's home directory as specified by your system:
#include <unistd.h>
#include <pwd.h>
struct passwd *pwd = getpwuid( getuid() );
/* need to duplicate the string - we don't know
what pw_dir points to */
const char *homeDir = strdup( pwd->pw_dir );
Error checking is left as an exercise...
On Linux and POSIX systems the home directory is often from the HOME environment variable. So you might code
snprintf(filename, sizeof(filename), "%s/%s_%s.sho",
getenv("HOME"), client, id);
Pedantically the getenv(3) could fail (or be wrong). But that rarely happens. See environ(7).
(You might check, and or use getpwuid(3) with getuid(2)...)
With setuid executables things could become interestingly complex. You would need to define more precisely what the home is, and code appropriately (this is left as an exercise).
Use getenv(3) to query the value of the HOME environment variable. For example, this prints my home directory:
#include <stdlib.h>
#include <stdio.h>
int main(void) {
printf("%s\n", getenv("HOME"));
return 0;
}
You can set your variable filename to the return value, and then write whatever data you want there.
This should work on any Unix-like system, including Linux, macOS, BSD, and probably many more.
I have a sandboxed Cocoa app that, during an export process, needs to run a third party command-line tool. This tool appears to be hardcoded to use /tmp for its temporary files; sandboxing doesn't permit access to this folder, so the export fails.
How can I get this tool to run? I don't have access to its source code, so I can't modify it to use NSTemporaryDirectory(), and it doesn't appear to respect the TMP or TEMPDIR environment variables. For reasons I don't understand, giving myself a com.apple.security.temporary-exception.files.absolute-path.read-write entitlement doesn't seem to work, either.
Is there some way to re-map folders within my sandbox? Is there some obscure trick I can use? Should I try to patch the tool's binary somehow? I'm at my wit's end here.
I was able to get user3159253's DYLD_INSERT_LIBRARIES approach to work. I'm hoping they will write an answer describing how that works, so I'll leave the details of that out and explain the parts that ended up being specific to this case.
Thanks to LLDB, elbow grease, and not a little help from Hopper, I was able to determine that the third-party tool used mkstemp() to generate its temporary file names, and some calls (not all) used a fixed template starting with /tmp. I then wrote a libtmphack.dylib that intercepted calls to mkstemp() and modified the parameters before calling the standard library version.
Since mkstemp() takes a pointer to a preallocated buffer, I didn't feel like I could rewrite a path starting with a short string like "/tmp" to the very long string needed to get to the Caches folder inside the sandbox. Instead, I opted to create a symlink to it called "$tmp" in the current working directory. This could break if the tool chdir()'d at an inopportune time, but fortunately it doesn't seem to do that.
Here's my code:
//
// libtmphack.c
// Typesetter
//
// Created by Brent Royal-Gordon on 8/27/14.
// Copyright (c) 2014 Groundbreaking Software. This file is MIT licensed.
//
#include "libtmphack.h"
#include <dlfcn.h>
#include <stdlib.h>
#include <unistd.h>
//#include <errno.h>
#include <string.h>
static int gbs_has_prefix(char * needle, char * haystack) {
return strncmp(needle, haystack, strlen(needle)) == 0;
}
int mkstemp(char *template) {
static int (*original_mkstemp)(char * template) = NULL;
if(!original_mkstemp) {
original_mkstemp = dlsym(RTLD_NEXT, "mkstemp");
}
if(gbs_has_prefix("/tmp", template)) {
printf("libtmphack: rewrote mkstemp(\"%s\") ", template);
template[0] = '$';
printf("to mkstemp(\"%s\")\n", template);
// If this isn't successful, we'll presume it's because it's already been made
symlink(getenv("TEMP"), "$tmp");
int ret = original_mkstemp(template);
// Can't do this, the caller needs to be able to open the file
// int retErrno = errno;
// unlink("$tmp");
// errno = retErrno;
return ret;
}
else {
printf("libtmphack: OK with mkstemp(\"%s\")\n", template);
return original_mkstemp(template);
}
}
Very quick and dirty, but it works like a charm.
Since #BrentRoyal-Gordon has already published a working solution I'm simply duplicating my comment which inspired him to produce the solution:
In order to fix a program behavior, I would intercept and override some system calls with the help of DYLD_INSERT_LIBRARIES and a custom shared library with a custom implementation of the given system calls.
The exact list of the syscalls which need to be overridden depends on nature of the application and can be studied with a number of tools built upon MacOS DTrace kernel facility. E.g. dtruss or Hopper. #BrentRoyal-Gordon has investigated that the app can be fixed solely with an /appropriate/ implementation of mkstemp.
That's it. I'm still not sure that I've deserved the bounty :)
Another solution would be to use chroot within the child process (or posix_spawn options) to change its root directory to a directory that is within your sandbox. Its “/tmp” will then be a “tmp” directory within that directory.
I'm coding in C on a linux system. I want to insert a USB flash drive, let udev create the dev nodes (at /dev/sdc and /dev/sdc1, for example), and take an action only when /dev/sdc appears. What I've been doing is thinking of this as a wait loop in my C application, waiting for a dev node to be created by the udev daemon. Something like the following:
if( /* /dev/sdc exists */)
{
do_something();
}
else
{
wait();
}
My first problem is, what C library function can go in my if() test to return a value for "/dev/sdc exists." My second problem is, am I simply approaching this wrongly? Should I be using a udev monitor structure to detect this straight from udev?
You may want to take a look at fstat() from the standard library.
This allows you to do a quick-and-dirty check on the presence/absence of the file and act upon that.
basically you need to:
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
....
struct stat s;
stat( "/pat/to/node" , &s );
if ( IS_BLK(s.st_mode) ) {
/* the file exists and is a block device */
}
This is not an elegant solution but answers your question.
The code might need some tuning because I didn't try it but it should do the trick.
Cheers.
You probably want to use udev rules.
Running external programs upon certain events
Yet another reason for writing udev rules is to run a particular program when a device is connected or disconnected. For example, you might want to execute a script to automatically download all of your photos from your digital camera when it is connected.
here is my problem: In C, I create the copy of a file (with some changes) This is done trivially via fopen(), getchar and putchar.
Copying the file is fine and the outputfile itself is what I want it to be.
My problem is: I assume that I will use this program often as sudo and then the resulting file has both another owner (root) as well as different permissions (execute rights are gone).
My question is: How can I copy the owner and permissions of the original file and then write them into the new one?
Use the fstat(2) system call to obtain the details about the owner and the permissions, and the fchmod(2) and fchown(2) system calls to set them. See an example in the setfile function of the *BSD cp(1) source code.
since you use fopen():
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
//fp is your source filepointer, fdest is your dest filepointer
//fn is the new filename you're copying to
struct stat fst;
//let's say this wont fail since you already worked OK on that fp
fstat(fileno(fp),&fst);
//update to the same uid/gid
fchown(fileno(fdest),fst.st_uid,fst.st_gid);
//update the permissions
fchmod(fileno(fdest),fst.st_mode);
as a quick note, you may want to fread() and fwrite() instead of f*char()'ing
Under linux use the libc fchmod and fchown
Manpages can be found here:
http://linux.die.net/man/2/fchmod
http://linux.die.net/man/2/fchown