Race condition with stat and mkdir in sequence - mkdir

Coverity complains of . toctou: Calling function mkdir that uses DIR after a check function. This can cause a time-of-check, time-of-use race condition
if (stat(DIR, &st) != 0)
{
if (mkdir(DIR, 0755) < 0)
{
return ERROR;
}
}
Is it good enough to change the code to ,I was using stat only for file exist check
if (mkdir(NDUID_DIR, 0755) < 0)
{
if(errno != EEXIST)
{
return ERROR;
}
}
Is there a better way to fix the code?

Both of your snippets appear to be incorrect and/or incomplete.
On OpenBSD, sys_mkdir would return -1, and set errno to EEXIST when the target file is present. However, that doesn't guarantee that the target file is a directory -- an existing regular file would still result in mkdir(2) returning the exact same EEXIST.
For guidance of the widely accepted approach, take a look at how mkdir(1) -p option is implemented across the BSDs (bin/mkdir/mkdir.c#mkpath in OpenBSD and NetBSD), all of which, on mkdir(2)'s error, appear to immediately call stat(2) to subsequently run the S_ISDIR macro to ensure that the existing file is a directory, and not just any other type of a file.

Related

Is it possible to enable exit on error behavior in an interactive Tcl shell?

I need to automate a huge interactive Tcl program using Tcl expect.
As I realized, this territory is really dangerous, as I need to extend the already existing mass of code, but I can't rely on errors actually causing the program to fail with a positive exit code as I could in a regular script.
This means I have to think about every possible thing that could go wrong and "expect" it.
What I currently do is use a "die" procedure instead of raising an error in my own code, that automatically exits. But this kind of error condition can not be catched, and makes it hard to detect errors especially in code not written by me, since ultimately, most library routines will be error-based.
Since I have access to the program's Tcl shell, is it possible to enable fail-on-error?
EDIT:
I am using Tcl 8.3, which is a severe limitation in terms of available tools.
Examples of errors I'd like to automatically exit on:
% puts $a(2)
can't read "a(2)": no such element in array
while evaluating {puts $a(2)}
%
% blublabla
invalid command name "blublabla"
while evaluating blublabla
%
As well as any other error that makes a normal script terminate.
These can bubble up from 10 levels deep within procedure calls.
I also tried redefining the global error command, but not all errors that can occur in Tcl use it. For instance, the above "command not found" error did not go through my custom error procedure.
Since I have access to the program's Tcl shell, is it possible to
enable fail-on-error?
Let me try to summarize in my words: You want to exit from an interactive Tcl shell upon error, rather than having the prompt offered again?
Update
I am using Tcl 8.3, which is a severe limitation in terms of available
tools [...] only source patches to the C code.
As you seem to be deep down in that rabbit hole, why not add another source patch?
--- tclMain.c 2002-03-26 03:26:58.000000000 +0100
+++ tclMain.c.mrcalvin 2019-10-23 22:49:14.000000000 +0200
## -328,6 +328,7 ##
Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp));
Tcl_WriteChars(errChannel, "\n", 1);
}
+ Tcl_Exit(1);
} else if (tsdPtr->tty) {
resultPtr = Tcl_GetObjResult(interp);
Tcl_GetStringFromObj(resultPtr, &length);
This is untested, the Tcl 8.3.5 sources don't compile for me. But this section of Tcl's internal are comparable to current sources, tested using my Tcl 8.6 source installation.
For the records
With a stock shell (tclsh), this is a little fiddly, I am afraid. The following might work for you (though, I can imagine cases where this might fail you). The idea is
to intercept writes to stderr (this is to where an interactive shell redirects error messages, before returning to the prompt).
to discriminate between arbitrary writes to stderr and error cases, one can use the global variable ::errorInfo as a sentinel.
Step 1: Define a channel interceptor
oo::class create Bouncer {
method initialize {handle mode} {
if {$mode ne "write"} {error "can't handle reading"}
return {finalize initialize write}
}
method finalize {handle} {
# NOOP
}
method write {handle bytes} {
if {[info exists ::errorInfo]} {
# This is an actual error;
# 1) Print the message (as usual), but to stdout
fconfigure stdout -translation binary
puts stdout $bytes
# 2) Call on [exit] to quit the Tcl process
exit 1
} else {
# Non-error write to stderr, proceed as usual
return $bytes
}
}
}
Step 2: Register the interceptor for stderr in interactive shells
if {[info exists ::tcl_interactive]} {
chan push stderr [Bouncer new]
}
Once registered, this will make your interactive shell behave like so:
% puts stderr "Goes, as usual!"
Goes, as usual!
% error "Bye, bye"
Bye, bye
Some remarks
You need to be careful about the Bouncer's write method, the error message has already been massaged for the character encoding (therefore, the fconfigure call).
You might want to put this into a Tcl package or Tcl module, to load the bouncer using package req.
I could imagine that your program writes to stderr and the errorInfo variable happens to be set (as a left-over), this will trigger an unintended exit.

How to get return error code of popen?

I want the error code which return by 'popen'.
Popen command is as below:
fp = popen("/system/bin/pgrep -f "/system/bin/ffmpeg -loglevel quiet -re -i /data/misc/qmmf/LiveStreaming_FIFO_1.264 -codec copy -f rtsp -rtsp_transport tcp rtsp://11.11.11.11:554/live/stream1?wowzatoken=B-VDkZmspHh-G49SQIKghznGAHryq5zNc3NE7KEDgx8=" ","r");
I am getting the PID of the currently running ffmpeg process and am reading this popen() using a file pointer in an array. Then, read the PID(char by chat and using atoi()) and kill that PID.
Checking the (fp == NULL), but not getting NULL pointer.
Mainly, I want to kill that ffmpeg process. Not all running ffmpeg process, but the specific one which distinguishes by its name.
Are there any other ways apart from this?
Any suggestions would helpful.
Assuming a Linux system, man 3 popen explains the programming interface. popen() will return NULL when fork/pipe call fail or memory cannot be allocated. On success I/O stream pointer is returned. The value of the variable doesn't change unless a new value is assigned to the variable.
To wait for the process to terminate, you should use pclose(), which will return -1 on error. Additionally, both functions set errno if an error occurs.
If you need to reliably get the child process' PID, you need to do fork() call in your code and use it's return value. Since you won't be using popen(), you also need to set up pipe(s) in your code.
I don't know that I fully understand your question, but this is what the man page says about popen() errors:
RETURN VALUE
The popen() function returns NULL if the fork(2) or pipe(2) call fails, or if it cannot allocate memory.
ERRORS
The popen() function does not set errno if memory allocation fails. If the underlying fork(2) or pipe(2) fails, errno is set appropriately. If the type argument is invalid, and this condition is detected, errno is set to EINVAL.
Are you checking errno in addition to checking the return value?

issue with a modification of youtube-dl in .zshrc

the code I have in my .zshrc is:
ytdcd () { #youtube-dl that automatically puts stuff in a specific folder and returns to the former working directory after.
cd ~/youtube/new/ && {
youtube-dl "$#"
cd - > /dev/null
}
}
ytd() { #sofar, this function can only take one page. so, i can only send one youttube video code per line. will modify it to accept multiple lines..
for i in $*;
do
params=" $params https://youtu.be/$i"
done
ytdcd -f 18 $params
}
so, on the commandline (terminal), when i enter ytd DFreHo3UCD0, i would like to have the video at https://youtu.be/DFreHo3UCD0 to be downloaded. the problem is that when I enter the command in succession, the system just tries to download the video for the previous command and rightly claims the download is complete.
For example, entering:
> ytd DFreHo3UCD0
> ytd L3my9luehfU
would not attempt to download the video for L3my9luehfU but only the video for DFreHo3UCD0 twice.
First -- there's no point to returning to the old directory for ytdcd: You can change to a new directory only inside a subshell, and then exec youtube-dl to replace that subshell with the application process:
This has fewer things to go wrong: Aborting the function's execution can't leave things in the wrong directory, because the parent shell (the one you're interactively using) never changed directories in the first place.
ytdcd () {
(cd ~/youtube/new/ && exec youtube-dl "$#")
}
Second -- use an array when building argument lists, not a string.
If you use set -x to log its execution, you'll see that your original command runs something like:
ytdcd -f 18 'https://youtu.be/one https://youtu.be/two https://youtu.be/three'
See those quotes? That's because $params is a string, passed as a single argument, not an array. (In bash -- or another shell following POSIX rules -- an unquoted string expansion would be string-split and glob-expanded, but zsh doesn't follow POSIX rules).
The following builds up an array of separate arguments and passes them individually:
ytd() {
local -a params=( )
local i
for i; do
params+=( "https://youtu.be/$i" )
done
ytdcd -f 18 "${params[#]}"
}
Finally, it's come up that you don't actually intend to pass all the URLs to just one youtube-dl instance. To run a separate instance per URL, use:
ytd() {
local i retval=0
for i; do
ytdcd -f 18 "$i" || retval=$?
done
return "$retval"
}
Note here that we're capturing non-success exit status, so as not to hide an error in any ytdcd instance other than the last (which would otherwise occur).
I would declare param as local, so that you are not appending url after urls...
You can try to add this awesome function to your .zshrc:
funfun() {
local _fun1="$_fun1 fun1!"
_fun2="$_fun2 fun2!"
echo "1 says: $_fun1"
echo "2 says: $_fun2"
}
To observe the thing ;)
EDIT (Explanation):
When sourcing shell script, you add it to you current environment, that is why you can run those function you define. So, when those function use variables, by default, those variable will be global and accessible from anywhere in your environment! Therefore, In this case param is defined globally for all the length of your shell session. Since you want to allow the download of several video at once, you are appending values to this global variable, which will grow all the time.
Enforcing local tells zsh to limit the scope of params to the function only.
Another solution is to reset the variable when you call the function.

CopyFile() on VxWorks

What is simple API for copy file(s) on VxWorks (look like a CopyFile() on Windows API)?
I assume you are talking about working in the command shell, so the commands may look like:
-> ls // lists the current directory contents
Myfile1
Myfile2
YourFile2.txt
value = 0 = 0x0 //return status of the ls command - executed w/o errors*
-> copy "Myfile1","/YourDirectory/Myfile1" // FORMAT: copy "src" , "dest"*
// NB: src & dest argument must be strings*
value = 0 = 0x0 // return status of copy command.
-> cd "/YourDirectory/" // change default directory - notice the trailing slash (/)
value = 0 = 0x0 // return status of cd command
-> ls
xyzfile
Myfile1
value = 0 = 0x0
I hope this helps
HadziJo
Generally, anything that can be executed at the shell can be called from a program other than the shell.
status = copy("Myfile1", "/YourDirectory/Myfile1");
if (status == OK) .....
You might look at the man page for xcopy as well depending on the functionality you need.
You can also use "cp" command on cmd shell (vxWorks 6.x), but that is not API, so probably doesn't answer your question exactly.
The best method I found is xcopy("fromPath", "toPath"). It will recursively (including folders and subfolders) copy (duplicate) everything fromPath toPath.
check out the VxWork Manual: http://www.vxdev.com/docs/vx55man/vxworks/ref/usrFsLib.html#xcopy

Executing a script from inside code in VxWorks 6.7

In VxWorks 5.5.1 you could run a script using the execute command. In VxWorks 6.7 the execute command is no longer supported. Does anyone now if there is a replacement? I am specifically talking about from inside code not command line.
Through much research it appears like there are a few ways to accomplish this but none is exactly the same as the execute command from before. As I stated in the comment below it turns out that the execute command is not an official API call.
1) shellCmdExec can be used but most be called from inside the shell task.
2) The solution we choose to employ - which is to call it from within our startup script
3) And a hack way:
fd = open("/y/startup.go", 0, 0) /* open the script you want to execute /
v=shellFromNameGet("tShell0") / Get the shell i.d. */
/* Use shellinOutGet to save off the standard in of the shell /
shellInOutSet (v, fd, -1, -1) / Set the standard in of the shell to the file */
/* Here you should restore the standard in (do a shellInOutGet beforehand). Do it after the shell is done with the script. I would say that your script should increrment a variable when ti is done. */
close(fd)
There's a solution in the VxWorks Kernel programmer's guide 6.7, the problem is that it did not work for me, but it could help you:
shellGenericInit ("INTERPRETER=Cmd", 0, NULL, &shellTaskName, FALSE, FALSE,fdScript, STD_OUT, STD_ERR); do
taskDelay (sysClkRateGet ());
while (taskNameToId (shellTaskName) != ERROR); close (fdScript);
Check Section 15.2.15 of the document.
You can do it in the serial driver layer. Try the following code. It shows how to send text to the shell's input.
For example,
pass_to_sio("memShow; ifconfig"); in your c code.
-> sp pass_to_sio, "memShow; ifconfig" in the shell.
pass_to_sio("< test.scr"); in your c code if you want to run a script file.
-> sp pass_to_sio, "< test.scr" in the shell if you want to run a script file.
void pass_to_sio(char *input)
{
int old_priority;
taskPriorityGet(taskIdSelf(),&old_priority);
taskPrioritySet(taskIdSelf(),250); /* task priority must be lower than tShell0 */
NS16550_CHAN *pChan = &ns16550Chan[0]; /* this line depends on your BSP */
while (input != NULL && *input != NULL)
{
(*pChan->putRcvChar) (pChan->putRcvArg, *input);
input++;
}
(*pChan->putRcvChar) (pChan->putRcvArg, '\r');
taskPrioritySet(taskIdSelf(),old_priority);
}