opensnoop with follow children mode - dtrace

opensnoop from DTrace can show which files are opened by a program/pid. It does not trace opens by forked/vforked children though. Related dtruss has this follow functionality.
Is there a way to tell opensnoop to also follow children?

-p option actually adds PID == pid check into generated script where pid is built in variable, representing current process id and PID is a -p option value.
There is an action in DTrace called progenyof which checks that current process is a child (not necessary direct) of a process, so simply replace that check in opensnoop:
--- /usr/dtrace/DTT/opensnoop Wed Jun 25 01:34:47 2014
+++ opensnoop Fri Jan 13 17:43:41 2017
## -199,7 +199,7 ##
/* check each filter */
(OPT_name == 1 && NAME == execname) ? self->ok = 1 : 1;
- (OPT_pid == 1 && PID == pid) ? self->ok = 1 : 1;
+ (OPT_pid == 1 && progenyof(PID)) ? self->ok = 1 : 1;
/* OPT_file is checked on return to ensure pathp is mapped */
}

Related

How do I change the creation time of a file to a newer date in Mac OS X?

On Mac OS X, using C and/or C++, I want to change the creation time of a file to an arbitrary date/time. I have found a number of solutions, notably this StackOverflow answer, that allow to set the creation time to an older date, but this is not sufficient for me - I also want to be able to set a newer date. Using the utimes() function therefore is not the solution I am looking for.
I know that setting a newer creation time must be possible somehow, because the SetFile utility from Apple's developer command line tools can do it, but so far my search-fu has failed to uncover any hints that bring me closer to a solution.
Does anyone know of a way how I can achieve my goal?
Why do I want to do this myself, why can't I use SetFile?
The SetFile command line utility is deprecated (see man SetFile), so it's bound to go away some time in the future
I want to create a utility that allows me to specify a time delta to add to/subtract from the current creation time. The SetFile utility does not have any convenient command line arguments to do this.
Last but not least: Curiosity!
Haven't tried it, but according to the docs, the NSURL resource value under the key NSURLCreationDateKey is read-write. Since you specified C or C++, you'd use the corresponding CFURL API. So, you'd call:
CFURLRef url = /* ... */
CFDateRef date = /* ... */
CFErrorRef error;
if (!CFURLSetResourcePropertyForKey(url, kCFURLCreationDateKey, date, &error))
/* handle error */;
EDIT: A minimal example
const char* fileName = "/path/to/file";
size_t fileNameStringLength = strlen(fileName);
Boolean isDirectory = false;
CFURLRef url = CFURLCreateFromFileSystemRepresentation(
kCFAllocatorDefault,
(const UInt8*)fileName,
fileNameStringLength,
isDirectory);
// Seconds since 1 January, 2001 00:00:00 GMT
CFAbsoluteTime absTime = CFAbsoluteTimeGetCurrent();
CFAbsoluteTime adjustedCreationTime = absTime - 3600;
CFDateRef date = CFDateCreate(
kCFAllocatorDefault,
adjustedCreationTime);
CFErrorRef error;
if (!CFURLSetResourcePropertyForKey(url, kCFURLCreationDateKey, date, &error))
{
fprintf(stderr, "an error occurred\n");
exit(1);
}
CFRelease(url);
CFRelease(date);
BTW, I have no idea if this is safe, secure, whatever. So, do this at your own risk.
On OS X, you can sort of do this by setting the time of the day to the future and then copying the file (and renaming it back). It is not the same file with its creation time modified; it is a copy with the creation time you set.
Some code (I got the code to set the time of the day from here):
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <copyfile.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
struct timeval tv_now, tv_set;
copyfile_state_t s;
struct stat st;
// retrieve original stat
if (stat(argv[2], &st) < 0)
perror("stat");
// get current time of day
if (gettimeofday(&tv_now, 0) == -1)
perror("gettimeofday");
// set time of day to +argv[1] days
tv_set = tv_now;
tv_set.tv_sec += 86400 * atoi(argv[1]);
if (settimeofday(&tv_set, 0) == -1)
perror("settimeofday to future");
// copy the file to a temporary, copy everythig except stat
s = copyfile_state_alloc();
if (copyfile(argv[2], ".eighty_eight_miles_per_hour", s, COPYFILE_ACL | COPYFILE_XATTR | COPYFILE_DATA) < 0)
perror("copy file");
copyfile_state_free(s);
// rename it back to original name
if (rename(".eighty_eight_miles_per_hour", argv[2]) < 0)
perror("rename file");
// restore file owner, group, and mode
if (chown(argv[2], st.st_uid, st.st_gid) < 0)
perror("chown");
if (chmod(argv[2], st.st_mode) < 0)
perror("chmod");
// reset current time of day
if (settimeofday(&tv_now, 0) == -1)
perror("settimeofday back to now");
return 0;
}
I call this program the flux_capacitor. The first command line argument is the number of days forward to set the file's creation date, and the second argument is the file name. You have to run this program as root to set the time.
Observe, as I send the delorean forward in time by 2 days.
[ronin:~/Documents/CPP] aichao% touch delorean
[ronin:~/Documents/CPP] aichao% ls -l delorean
-rw-r--r-- 1 aichao staff 0 Aug 10 11:43 delorean
[ronin:~/Documents/CPP] aichao% su
Password:
sh-3.2# ./flux_capacitor 2 delorean
sh-3.2# exit
exit
[ronin:~/Documents/CPP] aichao% ls -l delorean
-rw-r--r-- 1 aichao staff 0 Aug 12 2016 delorean
[ronin:~/Documents/CPP] aichao% date
Wed Aug 10 11:43:47 EDT 2016
and in the Finder:
Note that I only restore the original owner, group, and mode from the stat for the file. I don't think you can or want to do more than that, but I don't know. Obviously, links to the file will be broken.

Cleaning up children processes asynchronously

This is an example from <Advanced Linux Programming>, chapter 3.4.4. The programs fork() and exec() a child process. Instead of waiting for the termination of the process, I want the parent process to clean up the children process (otherwise the children process will become a zombie process) asynchronously. The can be done using the signal SIGCHLD. By setting up the signal_handler we can make the clean-up work done when the child process ends. And the code the following:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>
int spawn(char *program, char **arg_list){
pid_t child_pid;
child_pid = fork();
if(child_pid == 0){ // it is the child process
execvp(program, arg_list);
fprintf(stderr, "A error occured in execvp\n");
return 0;
}
else{
return child_pid;
}
}
int child_exit_status;
void clean_up_child_process (int signal_number){
int status;
wait(&status);
child_exit_status = status; // restore the exit status in a global variable
printf("Cleaning child process is taken care of by SIGCHLD.\n");
};
int main()
{
/* Handle SIGCHLD by calling clean_up_process; */
struct sigaction sigchld_action;
memset(&sigchld_action, 0, sizeof(sigchld_action));
sigchld_action.sa_handler = &clean_up_child_process;
sigaction(SIGCHLD, &sigchld_action, NULL);
int child_status;
char *arg_list[] = { //deprecated conversion from string constant to char*
"ls",
"-la",
".",
NULL
};
spawn("ls", arg_list);
return 0;
}
However, When I run the program in the terminal, the parent process never ends. And it seems that it doesn't execute the function clean_up_child_process (since it doesn't print out "Cleaning child process is taken care of by SIGCHLD."). What's the problem with this snippet of code?
The parent process immediately returns from main() after the child pid is returned from fork(), it never has the opportunity to wait for the child to terminate.
for GNU/Linux users
I already read this book. Although the book talked about this mechanism as a:
quote from 3.4.4 page 59 of the book:
A more elegant solution is to notify the parent process when a child terminates.
but it just said that you can use sigaction to handle this situation.
Here is a complete example of how to handle processes in this way.
First why do ever we use this mechanism? Well, since we do not want to synchronize all processes together.
real example
Imagine that you have 10 .mp4 files and you want to convert them to .mp3 files. Well, I junior user does this:
ffmpeg -i 01.mp4 01.mp3
and repeats this command 10 times. A little higher users does this:
ls *.mp4 | xargs -I xxx ffmpeg -i xxx xxx.mp3
This time, this command pipes all 10 mp4 files per line, each one-by-one to xargs and then they one by one is converted to mp3.
But I senior user does this:
ls *.mp4 | xargs -I xxx -P 0 ffmpeg -i xxx xxx.mp3
and this means if I have 10 files, create 10 processes and run them simultaneously. And there is BIG different. In the two previous command we had only 1 process; it was created then terminated and then continued to another one. But with the help of -P 0 option, we create 10 processes at the same time and in fact 10 ffmpeg commands are running.
Now the purpose of cleaning up children asynchronously becomes cleaner. In fact we want to run some new processes but the order of those process and maybe the exit status of them is not matter for us. In this way we can run them as fast as possible and reduce the time.
First you can see man sigaction for any more details you want.
Second seeing this signal number by:
T ❱ kill -l | grep SIGCHLD
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
sample code
objective: using the SIGCHLD to clean up child process
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <wait.h>
#include <unistd.h>
sig_atomic_t signal_counter;
void signal_handler( int signal_number )
{
++signal_counter;
int wait_status;
pid_t return_pid = wait( &wait_status );
if( return_pid == -1 )
{
perror( "wait()" );
}
if( WIFEXITED( wait_status ) )
{
printf ( "job [ %d ] | pid: %d | exit status: %d\n",signal_counter, return_pid, WEXITSTATUS( wait_status ) );
}
else
{
printf( "exit abnormally\n" );
}
fprintf( stderr, "the signal %d was received\n", signal_number );
}
int main()
{
// now instead of signal function we want to use sigaction
struct sigaction siac;
// zero it
memset( &siac, 0, sizeof( struct sigaction ) );
siac.sa_handler = signal_handler;
sigaction( SIGCHLD, &siac, NULL );
pid_t child_pid;
ssize_t read_bytes = 0;
size_t length = 0;
char* line = NULL;
char* sleep_argument[ 5 ] = { "3", "4", "5", "7", "9" };
int counter = 0;
while( counter <= 5 )
{
if( counter == 5 )
{
while( counter-- )
{
pause();
}
break;
}
child_pid = fork();
// on failure fork() returns -1
if( child_pid == -1 )
{
perror( "fork()" );
exit( 1 );
}
// for child process fork() returns 0
if( child_pid == 0 ){
execlp( "sleep", "sleep", sleep_argument[ counter ], NULL );
}
++counter;
}
fprintf( stderr, "signal counter %d\n", signal_counter );
// the main return value
return 0;
}
This is what the sample code does:
create 5 child processes
then goes to inner-while loop and pauses for receiving a signal. See man pause
then when a child terminates, parent process wakes up and calls signal_handler function
continue up to the last one: sleep 9
output: (17 means SIGCHLD)
ALP ❱ ./a.out
job [ 1 ] | pid: 14864 | exit status: 0
the signal 17 was received
job [ 2 ] | pid: 14865 | exit status: 0
the signal 17 was received
job [ 3 ] | pid: 14866 | exit status: 0
the signal 17 was received
job [ 4 ] | pid: 14867 | exit status: 0
the signal 17 was received
job [ 5 ] | pid: 14868 | exit status: 0
the signal 17 was received
signal counter 5
when you run this sample code, on the other terminal try this:
ALP ❱ ps -o time,pid,ppid,cmd --forest -g $(pgrep -x bash)
TIME PID PPID CMD
00:00:00 5204 2738 /bin/bash
00:00:00 2742 2738 /bin/bash
00:00:00 4696 2742 \_ redshift
00:00:00 14863 2742 \_ ./a.out
00:00:00 14864 14863 \_ sleep 3
00:00:00 14865 14863 \_ sleep 4
00:00:00 14866 14863 \_ sleep 5
00:00:00 14867 14863 \_ sleep 7
00:00:00 14868 14863 \_ sleep 9
As you can see a.out process has 5 children. And They are running simultaneously. Then whenever each of them terminates, kernel sends the signal SIGCHLD to their parent that is: a.out
NOTE
If we do not use pause or any mechanism so that the parent can wait for its children, then we will abandon the created processes and the upstart (= on Ubuntu or init) becomes parent of them. You can try it if you remove pause()
I'm using Mac, so my answer may be not quite relevant, but still. I compile without any options, so executable name is a.out.
I have the same experience with the console (the process doesn't seem to terminate), but I noticed that it's just terminal glitch, because you actually can just press Enter and your command line will be back, and actually ps executed from other terminal window doesn't show a.out, nor ls which it launched.
Also if I run ./a.out >/dev/null it finishes immediately.
So the point of the above is that everything actually terminates, just the terminal freezes for some reason.
Next, why it never prints Cleaning child process is taken care of by SIGCHLD.. Simply because the parent process terminates before child. The SIGCHLD signal can't be delivered to already terminated process, so the handler is never invoked.
In the book it's said that the parent process contiunes to do some other things, and if it really does then everything works fine, for example if you add sleep(1) after spawn().

Launching a service from c++ with execv

I'm trying to launch a linux service from a c++ and I do it successfully but one of my process is marked as "defunct" and I don't want that my parent process dies.
My code is (testRip.cpp):
int main()
{
char* zebraArg[2];
zebraArg[0] = (char *)"zebra";
zebraArg[1] = (char *)"restart";
char* ripdArg[2];
ripdArg[0] = (char *)"ripd";
ripdArg[1] = (char *)"restart";
pid_t ripPid;
pid_t zebraPid;
zebraPid = fork();
if(zebraPid == 0)
{
int32_t iExecvRes = 0;
iExecvRes = execv("/etc/init.d/zebra", zebraArg);
return 0;
if(iExecvRes == -1)
{
::syslog((LOG_LOCAL0 | LOG_ERR),
"zebra process failed \n");
}
}
else
{
while(1)
{
::syslog((LOG_LOCAL0 | LOG_ERR),
"running\n");
sleep(2);
}
}
}
The exit of ps -e command is:
9411 pts/1 00:00:00 testRip
9412 pts/1 00:00:00 testRip <defunct>
9433 ? 00:00:00 zebra
The /etc/init.d/zebra launches the service as daemon or something like that so I think this is the trick but:
Why there are 3 processes and one of them is marked as defunct?
What is wrong in my code?
How can I fix it?
Thanks in advance.
To remove zombies you the parent process must wait() its child or dies. If you need to make a non blocking wait() look at waitpid() with W_NOHANG flag.
Correctly forking a daemon process is hard in Unix and Linux because there are a lot of details to get right, and order is also important. I would suspect a combination of open file descriptors and not detatching the controlling terminal, in this case.
I would strongly suggest using a well-debugged implementation from another program - one of the reduced-functionality command line shells such as rsh or ksh may be a good choice, rather than trying to bake your own version.

Second setuid not permitted

If I call setuid in a loop to become root and reset the uid, this works only once.
I have the following code:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int my_func(int i) {
int current_uid = getuid();
int ret;
fprintf(stderr,
"### i=%d ###: My UID is: %d. My GID is: %d, before 'system(id)'\n",
i, current_uid, getgid());
system("/usr/bin/id");
fprintf(stderr,"\n\n### i=%d ###: before 'setuid(0)'\n", i);
if (setuid(0)) {
perror("setuid");
return 1;
}
fprintf(stderr,"after 'setuid(0)'\n\n");
//I am now root!
fprintf(stderr,
"### i=%d ###: I an now root: My UID is: %d. My GID is: %d, before 'system(id)'\n",
i, getuid(), getgid());
system("/usr/bin/id");
//Time to drop back to regular user priviledges
fprintf(stderr,"\n\nbefore 'setuid(%d)'\n",current_uid);
ret=setuid(current_uid);
fprintf(stderr,
"### i=%d ###: My UID is: %d. My GID is: %d, before 'system(id)\n",
i, getuid(), getgid());
system("/usr/bin/id");
}
int main(void) {
int i;
for (i=0;i<3;i++) {
my_func(i);
sleep(5);
fprintf(stderr,"\n\n");
}
return 0;
}
I set the SUID bit and run this program as ordinary user rose. The output is:
rose#condor:/home/rose/Txt/src/Test/C/Setuid(5)$ ll /usr/local/bin/multiple_setuid_test
-rws--x--x 1 root root 13589 11. Dez 08:41 /usr/local/bin/multiple_setuid_test*
rose#condor:/home/rose/Txt/src/Test/C/Setuid(6)$ /usr/local/bin/multiple_setuid_test
i=0 ###: My UID is: 1203. My GID is: 100, before 'system(id)'
uid=1203(rose) gid=100(users) Gruppen=100(users),4(adm),6(disk),7(lp),10(wheel),14(uucp),18(audio),19(cdrom),27(video),35(games),60(mysql),250(portage),1001(haldaemon),1002(plugdev),1008(scanner),1027(vboxusers),1028(kvm),1029(qemu),1036(gsm),1039(pulse-access),1040(pulse),1041(tuntap)
i=0 ###: before 'setuid(0)'
after 'setuid(0)'
i=0 ###: I an now root: My UID is: 0. My GID is: 100, before 'system(id)'
uid=0(root) gid=100(users) Gruppen=0(root),4(adm),6(disk),7(lp),10(wheel),14(uucp),18(audio),19(cdrom),27(video),35(games),60(mysql),100(users),250(portage),1001(haldaemon),1002(plugdev),1008(scanner),1027(vboxusers),1028(kvm),1029(qemu),1036(gsm),1039(pulse-access),1040(pulse),1041(tuntap)
before 'setuid(1203)'
i=0 ###: My UID is: 1203. My GID is: 100, before 'system(id)
uid=1203(rose) gid=100(users) Gruppen=100(users),4(adm),6(disk),7(lp),10(wheel),14(uucp),18(audio),19(cdrom),27(video),35(games),60(mysql),250(portage),1001(haldaemon),1002(plugdev),1008(scanner),1027(vboxusers),1028(kvm),1029(qemu),1036(gsm),1039(pulse-access),1040(pulse),1041(tuntap)
i=1 ###: My UID is: 1203. My GID is: 100, before 'system(id)'
uid=1203(rose) gid=100(users) Gruppen=100(users),4(adm),6(disk),7(lp),10(wheel),14(uucp),18(audio),19(cdrom),27(video),35(games),60(mysql),250(portage),1001(haldaemon),1002(plugdev),1008(scanner),1027(vboxusers),1028(kvm),1029(qemu),1036(gsm),1039(pulse-access),1040(pulse),1041(tuntap)
i=1 ###: before 'setuid(0)'
setuid: Operation not permitted
i=2 ###: My UID is: 1203. My GID is: 100, before 'system(id)'
uid=1203(rose) gid=100(users) Gruppen=100(users),4(adm),6(disk),7(lp),10(wheel),14(uucp),18(audio),19(cdrom),27(video),35(games),60(mysql),250(portage),1001(haldaemon),1002(plugdev),1008(scanner),1027(vboxusers),1028(kvm),1029(qemu),1036(gsm),1039(pulse-access),1040(pulse),1041(tuntap)
i=2 ###: before 'setuid(0)'
setuid: Operation not permitted
rose#condor:/home/rose/Txt/src/Test/C/Setuid(7)$ uname -a
Linux condor 3.12.4 #1 SMP Mon Dec 9 11:37:38 CET 2013 x86_64 Intel(R) Core(TM)2 CPU 6600 # 2.40GHz GenuineIntel GNU/Linux
Any hint is appreciated.
Linux (just like other unixes) store two values: your real user id, which is set by setuid, corresponds to your login, and is not changed by the s-bit, and your effective user id, which is set by seteuid, and gets changed when you execute a program that has the s-bit set. To be allowed to use setuid(), either your real or your effective user id need to be zero, but if you use setuid() to drop privileges (change from 0 to anything else), your effective user id will be changed as well. So your first setuid(1203) sets the effective user id to 1203 as well, which prevents you from regaining privileges later.
To change from and to root several times, you have to use the setreuid() call to swap both - setreuid(1203, 0) would change your real user id to 1203, and the effective user id to 0, thus allowing you to set the uid to 0 later.
Note that, as long as either of the two uids is 0, you have root privileges. With uid=1203 and euid=0, files you create will belong to you (user id 1203), but you'll still have root access. So check your program for security implications very carefully.
If you need to have root access every now and then throughout your program, it's much safer to create a pipe(), fork() off a child process that keeps root privileges, drop privileges in the parent process, and send some command down the pipe for the child to execute when you need something done as root. That way, you have to security audit only the child process, which is presumably much smaller than your whole program.
Once you give up root privileges by using setuid to change to a non-root user, you no longer have the privileges necessary to use setuid to become root once again.

ACE C++ Log in multiple files

I'm diving through ACE and, I'm logging message in a file using the ACE_ERROR macro.
And AFAIK, ACE_ERROR logs all the messages in the same file, regardless of their error level.
However, I actually need to write messages, according to their error level.
I did see the ACE_LOG_MSG->open() function however, what i understand is that when you already calling this function twice, the second time it will close the file you opened when you called the function at the beginning.
Suppose I have a list and I want to log it and, in this list, two adjacent items don't have the same error level.
Then I would be opening and closing files, wouldn't than affect my apps performance ?
So is there a way to keep those files open ?
Thanks !
Not closing the files you log to is particularly bad in debugging. If the application crashes with an open file, its contents may (and that happens rather often) get corrupted, leaving you with absolutely no information.
If you close the file properly, though, you're guaranteed to find at least some info there, possibly closer to the real issue. If you are concerned with performance, you should simply reduce log level, or if it's not feasible, you could perhaps offload the logging to the other process via (for example) TCP connection.
Anyway, don't optimize until you've measured! It might just be there'll be no impact, performance is a complicated problem which depends on lot of factors.
Another example to re-direct logging as per their logging priority, using a simple wrapper class.
Hope this is useful to someone.
Example program
#include "ace/Log_Msg.h"
#include "ace/streams.h"
// #Author: Gaurav A
// #Date: 2019OCT11
//
// Log each logging statement
// in file based on its priority
//
// eg: INFO logs goes to INFO.log
// DEBUG logs goes to DEBUG.log
class Logger
{
private:
ACE_OSTREAM_TYPE* m_infolog=nullptr;
ACE_OSTREAM_TYPE* m_debuglog=nullptr;
public:
Logger(void)
: m_infolog (new std::ofstream ("INFO.log")),
m_debuglog (new std::ofstream ("DEBUG.log"))
{
}
~Logger(void)
{
delete m_infolog;
delete m_debuglog;
}
int log (ACE_Log_Priority p,
const ACE_TCHAR* fmt,
...)
{
ssize_t final_result=0;
if (p == LM_DEBUG)
{
va_list argp;
va_start (argp, fmt);
ACE_LOG_MSG->msg_ostream (m_debuglog);
ACE_LOG_MSG->set_flags (ACE_Log_Msg::OSTREAM);
final_result = ACE_LOG_MSG->log (fmt, LM_DEBUG, argp);
va_end (argp);
}
else if (p == LM_INFO)
{
va_list argp;
va_start (argp, fmt);
ACE_LOG_MSG->msg_ostream (m_infolog);
ACE_LOG_MSG->set_flags (ACE_Log_Msg::OSTREAM);
final_result = ACE_LOG_MSG->log (fmt, LM_INFO, argp);
va_end (argp);
}
return final_result;
}
};
int
ACE_TMAIN (void)
{
Logger logger;
logger.log (LM_DEBUG, "I am a debug message no %d\n", 1);
logger.log (LM_INFO, "I am a info message no %d\n", 2);
logger.log (LM_DEBUG, "I am a debug message no %d\n", 3);
logger.log (LM_INFO, "I am a info message no %d\n", 4);
return 0;
}
Sample Output
[07:59:10]Host#User:~/acedir
$: ./logging_each_priority_in_its_own_file
I am a debug message no 1
I am a info message no 2
I am a debug message no 3
I am a info message no 4
[07:59:10]Host#User:~/acedir
$: ls -lrth
total 464K
-rw-r--r-- 1 aryaaur devusers 231 Oct 11 07:09 logging_each_priority_in_its_own_file.mpc
-rw-r--r-- 1 aryaaur devusers 5.6K Oct 11 07:29 GNUmakefile.logging_each_priority_in_its_own_file
-rw-r--r-- 1 aryaaur devusers 1.5K Oct 11 07:47 main_logging_each_priority_in_its_own_file_20191011.cpp
-rwxr-xr-x 1 aryaaur devusers 65K Oct 11 07:47 logging_each_priority_in_its_own_file
-rw-r--r-- 1 aryaaur devusers 50 Oct 11 07:59 INFO.log
-rw-r--r-- 1 aryaaur devusers 52 Oct 11 07:59 DEBUG.log
[07:59:10]Host#User:~/acedir
$: cat INFO.log
I am a info message no 2
I am a info message no 4
[07:59:10]Host#User:~/acedir
$: cat DEBUG.log
I am a debug message no 1
I am a debug message no 3
[07:59:10]Host#User:~/acedir
$: