I wish to dump the output of rsyslog(service) to some file at a selected
location.
Following is what I have tried :
1. Made changes to /etc/rsyslog.conf
#################
#### MODULES ####
#################
$ModLoad imfile
$ModLoad omprog <----- NEWLY ADDED ------>
$ModLoad imuxsock # provides support for local system logging
$ModLoad imklog # provides kernel logging support
#$ModLoad immark # provides --MARK-- message capability
###########################
#### GLOBAL DIRECTIVES ####
###########################
#
# Use traditional timestamp format.
# To enable high precision timestamps, comment out the following line.
#
$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
$ActionOMProgBinary /home/test/dmsg <----- NEWLY ADDED ------>
# Filter duplicated messages
dmsg : Is a C program that reads the lines from stdin and writes it to
file (/home/test/log_syslog_file)
I am expecting the output to be dumped to /home/test/log_syslog_file
But nothing happens.
code for dmsg (dmsg.c) ::
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
int main(){
char* lineptr;
size_t size = 0;
int fd = open("log_syslog_file", O_CREAT| O_WRONLY);
while(getline(&lineptr, &size, stdin)>0){
if(write(fd, lineptr, strlen(lineptr))<0){
fprintf(stderr, "write failure");
break;
}
}
free(lineptr);
close(fd);
return 0;
}
I am using Ubuntu 14.04
-------- EDIT ---------
After starting the rsyslog service,
I am giving the following command:
rsyslogd -c5 -d -n
When I use the following it works fine :
cat /var/log/syslog | ./dmsg
Thanks.
You've got at least one major bug in your code:
char* lineptr;
...
while(getline(&lineptr, &size, stdin)>0)
You never allocate memory for the string stored in *lineptr, but you don't tell getline() to allocate the memory for you, either. The resulting buffer overflow can result in all sorts of exciting bugs showing up before the inevitable crash (for example, in my test run, log_syslog_file got the permissions ---x--x--T).
First of all what #Mark said. Apart from that make sure that
you have something like
*.* :omprog:
in your rsyslog.conf.
This will redirect all the messages to your program.
Related
When I start a process under Linux, I can pass multiple input and output redirections. For example, if I'd like to have 14 output files, I'll do that:
command >f1 3>f2 4>f3 5>f4 6>f5 7>f6 8>f7 9>f8 10>f9 11>f10 12>f11 13>f12 14>f13 15>f14
Can my command know exactly how many such files were passed on the command line?
I know I can always add a command line option such as --count 14. I hope we don't have to.
There is no way to distinguish between redirections established on the command line and redirections which were already present in the execution environment.
For example, when utility util runs in the script
exec 4<file4
# ...
util 3<file3
it will see both fd 3 and fd 4 open for reading; it can't tell that only one was opened on the command line. If you care about that, which you probably should because there can be a number of these.
That aside, you can certainly figure out which fds are currently open, for example by cycling through all of them or examining the /proc/self/fd pseudo-directory. See this question for some example solutions to finding all the open file descriptors under Linux.
Can my command know exactly how many such files were passed on the command line?
Yes, if you order them sequentially. You can open() a dummy file (like /dev/null) and then subtract 3 from the fd returned by open to get the number of extra open files (other than stdin, stdout, and stderr) in the process. You can loop through them by starting from 3 and looping until you reach the dummy fd.
Example:
int dummy_fd = open("/dev/null", O_RDONLY);
printf("number of files open: %d\n", dummy_fd - 3);
for(int fd_ctr = 3; fd_ctr < fd; fd_ctr++)
/* ... */;
close(dummy_fd);
I know I can always add a command line option such as --count 14.
Good idea. While you're at it, how about you just pass the file names as command-line arguments, too?
Your process never sees any of those redirections. What happens is that the shell will connect up all the files to the equivalent file handles (opening them as necessary) and then run your code.
All your code can do is use the file handles that have been opened for you. Any tricks you use to find the maximum open handle inside your code will not work if you have gaps, like:
command >f1 5>f5
I suspect you'd be better off just using command line options to do this, something like:
command --output=file1 --output=file2 --output==file3
Then you have full control over how you handle those arguments in your code. For example, you can create a map of the files as you open each one.
It will also allow you to use different handles for other purposes, such as data files you need to access (I imagine you'd be rather miffed if you overwrote them with output data unrelated to the expected content simply because they were a file handle that was open).
On linux, you can just inspect the files in /proc/self/fd/. Each filename will correspond to an open fd.
Such example program:
#include <dirent.h>
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
int main() {
printf("Open fds:");
DIR *dir = opendir("/proc/self/fd/");
assert(dir != NULL);
struct dirent *d;
while (d = readdir(dir), d != NULL) {
if (d->d_type != DT_LNK) continue;
errno = 0;
const int i = atoi(d->d_name);
assert(errno == 0);
printf(" %d", i);
}
printf("\n");
}
When executes does this:
$ ./a.out 5>a 6>b 100>c
Open fds: 0 1 2 3 5 6 100
I'm trying to get my own ip addres with C.
The idea is to get the output of "ifconfig", put it in a .txt file and extract the inet and the inet6 values.
I stack trying to write the ifconfig output in the .txt file:
#include <stdio.h>
#include <stdlib.h>
#include <string>
int main ()
{
char command[1000];
strcpy(command, "ifconfig");
system(command);
FILE *fp = fopen("textFile.txt", "ab+");
//... how to write the output of 'system(command)' in the textFile.txt?
fclose(fp);
//... how to scraping a file .text in C ???
return 0;
}
Thank you for any help and suggestion,
Stefano
You actually want to use system calls to accomplish this - rather than running the ifconfig command.
There's a similar question here for Linux: using C code to get same info as ifconfig
(Since ifconfig is a Linux command, I'm assuming you're asking about Linux).
The general gist was to use the ioctl() system call.
Otherwise, you'll be forking your process to split it into two, creating a pipe from the output of the child to the input of the parent, and calling exec on the child in order to replace it with "ifconfig". Then, you'll have to parse the string if you want to get anything useful out of it.
In Linux, use man 7 netdevice describes the interface you can use, as Anish Goyal already answered.
This is very simple and robust, and uses very little resources (since it is just a few syscalls). Unfortunately, it is Linux specific, and makes the code nonportable.
It is possible to do this portably. I describe the portable option here, because the Linux-specific one is rather trivial. Although the approach is quite complicated for just obtaining the local host IP addresses, the pattern is surprisingly often useful, because it can hide system-specific quirks, while easily allowing system administrators to customize the behaviour.
The idea for a portable solution is that you use a small helper program or shell script to obtain the information, and have it output the information in some easy-to-parse format to your main program. If your application is named yourapp, it is common to install such helpers in /usr/lib/yourapp/, say /usr/lib/yourapp/local-ip-addresses.
Personally, I'd recommend using a shell script (so that system admins can trivially edit the helpers if they need to customize the behaviour), and an output format where each interface is on its own line, fields separated by spaces, perhaps
inet interface-name ipv4-address [ hostname ]*
inet6 interface-name ipv6-address [ hostname ]*
i.e. first token specifies the address family, second token the interface name, third the address, optionally followed by the hostnames or aliases corresponding to that address.
As to the helper program/shell script itself, there are two basic approaches:
One-shot
For example, parsing LANG=C LC_ALL=C ip address output in Linux.
The program/script will exit after the addresses have been printed.
Continuous
The program/script will print the ip address information, but instead of exiting, it will run as long as the pipe stays open, providing updates if interfaces are taken down or come up.
In Linux, a program/script could use DBUS or NetworkManager to wait for such events; it would not need to poll (that is, repeatedly check the command output).
A shell script has the extra benefit that it can support multiple distributions, even operating systems (across POSIX systems at least), at the same time. Such scripts often have a similar outline:
#!/bin/sh
export LANG=C LC_ALL=C
case "$(uname -s)" in
Linux)
if [ -x /bin/ip ]; then
/bin/ip -o address | awk \
'/^[0-9]*:/ {
addr = $4
sub(/\/.*$/, "", addr)
printf "%s %s %s\n", $3, $2, addr
}'
exit 0
elif [ -x /sbin/ifconfig ]; then
/sbin/ifconfig | awk \
'BEGIN {
RS = "[\t\v\f\r ]*\n"
FS = "[\t\v\f ]+"
}
/^[0-9A-Za-z]/ {
iface = $1
}
/^[\t\v\f ]/ {
if (length(iface) > 0)
for (i = 1; i < NF-1; i++)
if ($i == "inet") {
addr = $(i+1)
sub(/^addr:/, "", addr)
printf "inet %s %s\n", iface, addr
} else
if ($i == "inet6") {
addr = $(i+2)
sub(/\/.*$/, "", addr)
printf "inet6 %s %s\n", iface, addr
}
}'
exit 0
fi
;;
# Other systems?
esac
printf 'Cannot determine local IP addresses!\n'
exit 1
The script sets the locale to C/POSIX, so that the output of external commands will be in the default locale (in English and so on). uname -s provides the kernel name (Linux for Linux), and further checks can be done using e.g. the [ shell command.
I only implemented the scriptlet for Linux, because that's the machine I'm on right now. (Both ip and ifconfig alternatives work on my machine, and provide the same output -- although you cannot expect to get the interfaces in any specific order.)
The situations where a sysadmin might need to edit this particular helper script includes as-yet-unsupported systems, systems with new core tools, and systems that have interfaces that should be excluded from normal interface lists (say, those connected to a internal sub-network that are reserved for privileged purposes like DNS, file servers, backups, LDAP, and so on).
In the C application, you simply execute the external program or shell script using popen("/usr/lib/yourapp/local-ip-addresses", "r"), which provides you a FILE * that you can read as if it was a file (except you cannot seek or rewind it). After you have read everything from the pipe, pclose() the handle:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
/* ... */
FILE *in;
char *line_ptr = NULL;
size_t line_size = 0;
ssize_t line_len;
int status;
in = popen("/usr/lib/myapp/local-ip-addresses", "r");
if (!in) {
fprintf(stderr, "Cannot determine local IP addresses: %s.\n", strerror(errno));
exit(EXIT_FAILURE);
}
while (1) {
line_len = getline(&line_ptr, &line_size, in);
if (line_len < 1)
break;
/* Parse line_ptr */
}
free(line_ptr);
line_ptr = NULL;
line_size = 0;
if (ferror(in) || !feof(in)) {
pclose(in);
fprintf(stderr, "Read error while obtaining local IP addresses.\n");
exit(EXIT_FAILURE);
}
status = pclose(in);
if (!WIFEXITED(status) || WEXITSTATUS(status)) {
fprintf(stderr, "Helper utility /usr/lib/myapp/local-ip-addresses failed to obtain local IP addresses.\n");
exit(EXIT_FAILURE);
}
I omitted the parsing code, because there are so many alternatives -- strtok(), sscanf(), or even a custom function that splits the line into tokens (and populates an array of pointers) -- and my own preferred option is the least popular one (last one, a custom function).
While the code needed for just this task is almost not worth the while, applications that use this approach tend to use several of such helpers. Then, of course, it makes sense to choose the piped data format in a way that allows easy parsing, but supports all use cases. The amortized cost is then much easier to accept, too.
I am just beginning to toy around with combining ncurses and C to develop a very minimal TUI. The purpose of the TUI is to greet users with a basic login/welcome screen. The goal would be to display basic system information like the operating system, available memory, IP address, etc. Nothing beyond read-only.
What would be the best way to go about doing this? The part I'm struggling with is interfacing the shell commands like df, ls, ifconfig, etc with variables that I can then display or print in ncurses and C. I know something like this can be done, as well as calling the system command with a string, but this seems somewhat bulky:
#include <ncurses.h>
#include <stdlib.h>
#include <stdio.h>
int main(void) {
FILE *pp;
initscr();
cbreak();
if ((pp = popen("df", "r")) != 0) {
char buffer[BUFSIZ];
while (fgets(buffer, sizeof(buffer), pp) != 0) {
addstr(buffer);
}
pclose(pp);
}
getch();
return EXIT_SUCCESS;
}
Are there any methods to execute a command in the command line from within a C program and then selectively access the output of that command for later display? Or is this information generally stored somewhere in a parseable file on the machine? I'm new to trying to pull system information/use the command line in a "TUI" sense and any help would be appreciated. Thanks so much in advance!
The idea with the pipe is good and simple, anything else would lack at least one of "good and simple", most likely both. But your other question is about the availability of certain system informations. Well, these informations are wildly dispersed and the exact place depends on the operating system actually in use.
For the common, non-specialized Linux system: for the filesystem it is /etc/mtab (and use statfs() for the details) and many system informations are in /proc. If you need more, it gets complicated.
It is already quite complicated even if you want to build a simplified version of df for example (original code of df in $COREUTILS/src/df.c). Instead of just running df and read from a pipe, you have to do
read /etc/mtab and find the mount points (system might not have /etc/mtab although it should)
run statfs() on every mountpoint and print the result
you'll need over 100 error-prone lines of C-code for this, even if you skip everything fancy. And that for printing the filesystem alone.
No, just read the output of the old and well tested programs from a pipe, it's the easiest way.
EDIT:
You use the full shell if you use the pipe. That means that you can use all other tools, too.
To make it simpler to test, here is a simplified version without ncurses (making it much more complicated ;-) ), just for playing at the commandline.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUF_SIZE 256
// ALL CHECKS OMMITTED!
char *sub_exec(const char *command)
{
char buf[BUF_SIZE];
char *ret;
size_t mem_alloc, mem_needed;
ret = malloc(4 * BUF_SIZE * sizeof(char));
// or use calloc or set \0 manually
memset(ret, '\0', 4 * BUF_SIZE);
// memory allocated
mem_alloc = 4 * BUF_SIZE;
// memory needed
mem_needed = 0;
// open the pipe read-only
FILE *out_pipe = popen(command, "r");
// until end of the output of the pipe (EOF)
while (!feof(out_pipe)) {
// read a chunk of the output
if (fgets(buf, BUF_SIZE, out_pipe) != NULL) {
mem_needed += BUF_SIZE;
if (mem_alloc < mem_needed) {
// no fancy algorithms
ret = realloc(ret, mem_needed + 4 * BUF_SIZE);
mem_alloc = mem_needed * 2;
}
// and conatenate it to the result
strncat(ret, buf, BUF_SIZE);
}
}
pclose(out_pipe);
// You may or may not readjust the memory used
// ret = realloc(ret, strlen(ret) + 1);
return ret;
}
int main(int argc, char **argv)
{
char *str_from_pipe;
if (argc < 2)
fprintf(stderr, "Usage: %s command\n", argv[0]);
str_from_pipe = sub_exec(argv[1]);
printf("%s\n", str_from_pipe);
free(str_from_pipe);
exit(EXIT_SUCCESS);
}
You can do simple things here, like
./readpipe "cat win*c | tr -d '\015' | perl -0777 -pe 's{/\*.*?\*/}{}gs' | indent -"
(concatenate all C-files, strip the \rs, strip most comments and run it through indent(1))
Or with df. Say you want the file systems with actual data in it, not tempfs or alike, so:
./readpipe "df -P -h -t ext4"
That prints here:
Filesystem Size Used Avail Use% Mounted on
/dev/sda2 48G 33G 14G 71% /
/dev/sda3 861G 761G 56G 94% /home
(quite full, as it seems)
You can use it as it is or massage it further:
./readpipe "df -h -t ext4 --output=target,fstype,size,used,avail|awk '{if(NR>1)print}'"
Prints:
/ ext4 48G 33G 14G
/home ext4 861G 761G 56G
Caveat:
./readpipe "df -h -t ext4 --output=target,fstype,size,used,avail| sed -n '1!p'"
does not work, you need to exchange the kind of quotes (but it is not always that simple)
./readpipe 'df -h -t ext4 --output=target,fstype,size,used,avail | sed -n "1!p"'
To be able to split the entries with e.g.: strtok(3) replace all whitespace with single tabs
./readpipe 'df -h -t ext4 --output=target,fstype,size,used,avail | sed -n "1!p" | sed -e "s/[ ]\+/\t/g"'
(yes, there are much more elegant ways to do it, but it is good enough)
More useful information in the files/directories about the CPU(s)
/sys/devices/system/cpu/cpu*/cpufreq/
/sys/devices/system/cpu/cpu*/cache/
Or by lscpu. There are many ls* programs that are useful, like lspci, lsusb, lskat…no, wait, that's something different, and not to forget lsblk (lists the block devices incl. partitions if avail.). A quite complete list of the installed hardware (some info need root-rights but it is already quite extensive without) is available with the help of lshw and uname is for information about the OS, also: free for the memory consumption, and many, many more. Most, if not all allow for some kind of formatting, see the respective manpages for the gory details.
If you tell me what you you need, I can tell you where to find it (he says boldly ;-) ). Just ask in a comment below, I'll add it.
I'm trying to make a simple userspace program that dynamically generates file contents when a file is read, much like a virtual filesystem. I know there are programs like FUSE, but they seem a bit heavy for what I want to do.
For example, a simple counter implementation would look like:
$ cat specialFile
0
$ cat specialFile
1
$ cat specialFile
2
I was thinking that specialFile could be a named pipe, but I haven't had much luck. I was also thinking select may help here, but I'm not sure how I would use it. Am I missing some fundamental concept?
#include <stdio.h>
int main(void)
{
char stdoutEmpty;
char counter;
while (1) {
if (stdoutEmpty = feof(stdout)) { // stdout is never EOF (empty)?
printf("%d\n", counter++);
fflush(stdout);
}
}
return 0;
}
Then usage would be something like:
shell 1 $ mkfifo testing
shell 1 $ ./main > testing
shell 2 $ cat testing
# should be 0?
shell 2 $ cat testing
# should be 1?
You need to use FUSE. A FIFO will not work, because either your program keeps pushing content to stdout (in which case cat will never stop), or it closes stdout, in which case you obviously can't write to it anymore.
I have a simple task to accomplish with this routine where, all it has to do is, open the file, append data from a buffer & close.
I am using 'open' & 'write' for that purpose on a linux machine. Although the return code after 'write()' is positive, the file size does not increase and it is always empty. I ma pulling my hair to figure out what the issue with the below code. Thought some fresh eyes can shed some light.
#define BIT_Q_FILE ".\\bitq.dat"
int BQWrite(void *p)
{
int fd ;
int rc = -1 ;
fd = open(BIT_Q_FILE, O_RDWR | O_APPEND ) ;
if (fd < 0)
return -1;
memset(&BITQBuff,0,sizeof(typeBITQFile));
memcpy(&BITQBuff.pBitQueue,p,sizeof(typeBITQueue));
rc = write(fd, &BITQBuff,sizeof(typeBITQFile)) ;
close(fd) ;
if(rc!=sizeof(typeBITQFile))
{
return -1;
}
rc = sizeof(typeBITQueue);
return rc ;
}
I got your problem right here:
#define BIT_Q_FILE ".\\bitq.dat"
You've hit a trifecta of Windows-to-Unix porting gotchas:
The directory separator on Unix is /, not \.
But Unix is perfectly happy to let you put \ in the middle of a file name. (The only bytes — and I really mean bytes, not characters — that cannot appear in a pathname component are those with the values 0x2F and 0x00.)
Unix is also perfectly happy to let a file name begin with a dot; however, by default ls does not print any file names that begin with a dot.
So you are expecting data to be written to a file named bitq.dat in the current directory, but it is actually being written to a file named .\bitq.dat, still in the current directory. That file is hidden by default, so it looks like the data is disappearing into thin air. ls -a will reveal the hidden file, and rm .\\bitq.dat will delete it. To fix your code, just change the define to
#define BIT_Q_FILE "bitq.dat"
It is not necessary to put a leading ./ on the path passed to open.
This may not be the only problem with your code, but I don't see anything else obviously wrong. If you need more help, please post a new question with a complete, minimal test program that people can compile and run for themselves.
Try change
#define BIT_Q_FILE ".\\\bitq.dat"
to
#define BIT_Q_FILE "./bitq.dat"