Solved I need some guidance with bare-bone C programming in FreeBSD

Alright, I will start by showing what I'm trying to do, and then I'll ask a couple of questions:

I'm trying to create a small tool (similar to the standard ls CLI) trying to learn a few new things about UNIX C programming under BSD. I'm following a book I won't reveal here (not sure if sharing its title would actually infringe forum's regulations (please correct me if I'm wrong)).

I have a main .c file I compiled using clang, like so: cc -o myls ftest.c, the source of my ftest.c file looks like this:

Code:
#include "apue.h"
#include <dirent.h>

int main(int argc, char *argv[]) {

   DIR *dp;
   struct dirent *dirp;

   if (argc != 2)
     // err_quit("usage: ls directory_name");
   if ((dp = opendir(argv[1])) == NULL)
     // err_sys("can't open %s", argv[1]);
   while ((dirp = readdir(dp)) != NULL)
      printf("%s\n", dirp->d_name);

   closedir(dp);
   exit(0);

}

This is the source of the apue.c header file:

Code:
/*
* Our own header, to be included before all standard system headers.
*/
#ifndef    _APUE_H
#define    _APUE_H

#define _POSIX_C_SOURCE 200809L

#if defined(SOLARIS)        /* Solaris 10 */
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 700
#endif

#include <sys/types.h>        /* some systems still require this */
#include <sys/stat.h>
#include <termios.h>    /* for winsize */
#if defined(MACOS) || !defined(TIOCGWINSZ)
#include <sys/ioctl.h>
#endif

#include <stdio.h>        /* for convenience */
#include <stdlib.h>        /* for convenience */
#include <stddef.h>        /* for offsetof */
#include <string.h>        /* for convenience */
#include <unistd.h>        /* for convenience */
#include <signal.h>        /* for SIG_ERR */

#define    MAXLINE    4096            /* max line length */

/*
* Default file access permissions for new files.
*/
#define    FILE_MODE    (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

/*
* Default permissions for new directories.
*/
#define    DIR_MODE    (FILE_MODE | S_IXUSR | S_IXGRP | S_IXOTH)

typedef    void    Sigfunc(int);    /* for signal handlers */

#define    min(a,b)    ((a) < (b) ? (a) : (b))
#define    max(a,b)    ((a) > (b) ? (a) : (b))

/*
* Prototypes for our own functions.
*/
char    *path_alloc(size_t *);                /* {Prog pathalloc} */
long    open_max(void);                    /* {Prog openmax} */

int        set_cloexec(int);                    /* {Prog setfd} */
void    clr_fl(int, int);
void    set_fl(int, int);                    /* {Prog setfl} */

void    pr_exit(int);                        /* {Prog prexit} */

void    pr_mask(const char *);                /* {Prog prmask} */
Sigfunc    *signal_intr(int, Sigfunc *);        /* {Prog signal_intr_function} */

void    daemonize(const char *);            /* {Prog daemoninit} */

void    sleep_us(unsigned int);            /* {Ex sleepus} */
ssize_t    readn(int, void *, size_t);        /* {Prog readn_writen} */
ssize_t    writen(int, const void *, size_t);    /* {Prog readn_writen} */

int        fd_pipe(int *);                    /* {Prog sock_fdpipe} */
int        recv_fd(int, ssize_t (*func)(int,
                const void *, size_t));    /* {Prog recvfd_sockets} */
int        send_fd(int, int);                    /* {Prog sendfd_sockets} */
int        send_err(int, int,
                 const char *);            /* {Prog senderr} */
int        serv_listen(const char *);            /* {Prog servlisten_sockets} */
int        serv_accept(int, uid_t *);            /* {Prog servaccept_sockets} */
int        cli_conn(const char *);            /* {Prog cliconn_sockets} */
int        buf_args(char *, int (*func)(int,
                 char **));                /* {Prog bufargs} */

int        tty_cbreak(int);                    /* {Prog raw} */
int        tty_raw(int);                        /* {Prog raw} */
int        tty_reset(int);                    /* {Prog raw} */
void    tty_atexit(void);                    /* {Prog raw} */
struct termios    *tty_termios(void);            /* {Prog raw} */

int        ptym_open(char *, int);            /* {Prog ptyopen} */
int        ptys_open(char *);                    /* {Prog ptyopen} */
#ifdef    TIOCGWINSZ
pid_t    pty_fork(int *, char *, int, const struct termios *,
                 const struct winsize *);    /* {Prog ptyfork} */
#endif

int        lock_reg(int, int, int, off_t, int, off_t); /* {Prog lockreg} */

#define    read_lock(fd, offset, whence, len) \
            lock_reg((fd), F_SETLK, F_RDLCK, (offset), (whence), (len))
#define    readw_lock(fd, offset, whence, len) \
            lock_reg((fd), F_SETLKW, F_RDLCK, (offset), (whence), (len))
#define    write_lock(fd, offset, whence, len) \
            lock_reg((fd), F_SETLK, F_WRLCK, (offset), (whence), (len))
#define    writew_lock(fd, offset, whence, len) \
            lock_reg((fd), F_SETLKW, F_WRLCK, (offset), (whence), (len))
#define    un_lock(fd, offset, whence, len) \
            lock_reg((fd), F_SETLK, F_UNLCK, (offset), (whence), (len))

pid_t    lock_test(int, int, off_t, int, off_t);        /* {Prog locktest} */

#define    is_read_lockable(fd, offset, whence, len) \
            (lock_test((fd), F_RDLCK, (offset), (whence), (len)) == 0)
#define    is_write_lockable(fd, offset, whence, len) \
            (lock_test((fd), F_WRLCK, (offset), (whence), (len)) == 0)

void    err_msg(const char *, ...);            /* {App misc_source} */
void    err_dump(const char *, ...) __attribute__((noreturn));
void    err_quit(const char *, ...) __attribute__((noreturn));
void    err_cont(int, const char *, ...);
void    err_exit(int, const char *, ...) __attribute__((noreturn));
void    err_ret(const char *, ...);
void    err_sys(const char *, ...) __attribute__((noreturn));

void    log_msg(const char *, ...);            /* {App misc_source} */
void    log_open(const char *, int, int);
void    log_quit(const char *, ...) __attribute__((noreturn));
void    log_ret(const char *, ...);
void    log_sys(const char *, ...) __attribute__((noreturn));
void    log_exit(int, const char *, ...) __attribute__((noreturn));

void    TELL_WAIT(void);        /* parent/child from {Sec race_conditions} */
void    TELL_PARENT(pid_t);
void    TELL_CHILD(pid_t);
void    WAIT_PARENT(void);
void    WAIT_CHILD(void);

#endif    /* _APUE_H */

Now, all is well with the compilation process, I don't get any errors or warnings. But, when I try to run my myls I get this error: PR Segmentation fault (core dumped) . I ran truss ./myls and this is what I get back
Code:
mmap(0x0,32768,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANON,-1,0x0) = 34366136320 (0x80061a000)
issetugid(0x800819f30,0x7fffffffefca,0x40,0x0,0xffff80080081af66,0x0) = 0 (0x0)
lstat("/etc",{ mode=drwxr-xr-x ,inode=2808960,size=2048,blksize=32768 }) = 0 (0x0)
lstat("/etc/libmap.conf",{ mode=-rw-r--r-- ,inode=2809041,size=112,blksize=32768 }) = 0 (0x0)
open("/etc/libmap.conf",O_CLOEXEC,01760)    = 3 (0x3)
fstat(3,{ mode=-rw-r--r-- ,inode=2809041,size=112,blksize=32768 }) = 0 (0x0)
mmap(0x0,112,PROT_READ,MAP_PRIVATE,3,0x0)    = 34366169088 (0x800622000)
close(3)                    = 0 (0x0)
lstat("/usr",{ mode=drwxr-xr-x ,inode=8828160,size=512,blksize=32768 }) = 0 (0x0)
lstat("/usr/local",{ mode=drwxr-xr-x ,inode=8828171,size=512,blksize=32768 }) = 0 (0x0)
lstat("/usr/local/etc",{ mode=drwxr-xr-x ,inode=9000309,size=1024,blksize=32768 }) = 0 (0x0)
lstat("/usr/local/etc/libmap.d",0x7fffffffb7b8)    ERR#2 'No such file or directory'
munmap(0x800622000,112)                = 0 (0x0)
open("/var/run/ld-elf.so.hints",O_CLOEXEC,00)    = 3 (0x3)
read(3,"Ehnt\^A\0\0\0\M^@\0\0\0X\0\0\0\0"...,128) = 128 (0x80)
lseek(3,0x80,SEEK_SET)                = 128 (0x80)
read(3,"/lib:/usr/lib:/usr/lib/compat:/u"...,88) = 88 (0x58)
close(3)                    = 0 (0x0)
access("/lib/libc.so.7",0)            = 0 (0x0)
open("/lib/libc.so.7",O_CLOEXEC,030363770)    = 3 (0x3)
fstat(3,{ mode=-r--r--r-- ,inode=12118669,size=1567216,blksize=32768 }) = 0 (0x0)
mmap(0x0,4096,PROT_READ,MAP_PRIVATE|MAP_PREFAULT_READ,3,0x0) = 34366169088 (0x800622000)
mmap(0x0,3772416,PROT_NONE,MAP_PRIVATE|MAP_ANON|MAP_NOCORE,-1,0x0) = 34368237568 (0x80081b000)
mmap(0x80081b000,1458176,PROT_READ|PROT_EXEC,MAP_PRIVATE|MAP_FIXED|MAP_NOCORE|MAP_PREFAULT_READ,3,0x0) = 34368237568 (0x80081b000)
mmap(0x800b7f000,49152,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_FIXED|MAP_PREFAULT_READ,3,0x164000) = 34371792896 (0x800b7f000)
mmap(0x800b8b000,167936,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_FIXED|MAP_ANON,-1,0x0) = 34371842048 (0x800b8b000)
munmap(0x800622000,4096)            = 0 (0x0)
close(3)                    = 0 (0x0)
munmap(0x800621000,4096)            = 0 (0x0)
mmap(0x0,102400,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANON,-1,0x0) = 34366164992 (0x800621000)
sysarch(0x81,0x7fffffffd1a8,0x4,0x0,0xffffffffffab4080,0x8080808080808080) = 0 (0x0)
sigprocmask(SIG_BLOCK,SIGHUP|SIGINT|SIGQUIT|SIGKILL|SIGPIPE|SIGALRM|SIGTERM|SIGURG|SIGSTOP|SIGTSTP|SIGCONT|SIGCHLD|SIGTTIN|SIGTTOU|SIGIO|SIGXCPU|SIGXFSZ|SIGVTALRM|SIGPROF|SIGWINCH|SIGINFO|SIGUSR1|SIGUSR2,0x0) = 0 (0x0)
sigprocmask(SIG_SETMASK,0x0,0x0)        = 0 (0x0)
readlink("/etc/malloc.conf",0x7fffffffc8d0,1024) ERR#2 'No such file or directory'
issetugid(0x80095693e,0x7fffffffc8d0,0xffffffffffffffff,0x0,0x3a,0xffffffff0fffffff) = 0 (0x0)
mmap(0x0,4194304,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANON,-1,0x0) = 34372009984 (0x800bb4000)
munmap(0x800bb4000,4194304)            = 0 (0x0)
mmap(0x0,8384512,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANON,-1,0x0) = 34372009984 (0x800bb4000)
munmap(0x800bb4000,311296)            = 0 (0x0)
munmap(0x801000000,3878912)            = 0 (0x0)
sigprocmask(SIG_BLOCK,SIGHUP|SIGINT|SIGQUIT|SIGKILL|SIGPIPE|SIGALRM|SIGTERM|SIGURG|SIGSTOP|SIGTSTP|SIGCONT|SIGCHLD|SIGTTIN|SIGTTOU|SIGIO|SIGXCPU|SIGXFSZ|SIGVTALRM|SIGPROF|SIGWINCH|SIGINFO|SIGUSR1|SIGUSR2,0x0) = 0 (0x0)
sigprocmask(SIG_SETMASK,0x0,0x0)        = 0 (0x0)
sigprocmask(SIG_BLOCK,SIGHUP|SIGINT|SIGQUIT|SIGKILL|SIGPIPE|SIGALRM|SIGTERM|SIGURG|SIGSTOP|SIGTSTP|SIGCONT|SIGCHLD|SIGTTIN|SIGTTOU|SIGIO|SIGXCPU|SIGXFSZ|SIGVTALRM|SIGPROF|SIGWINCH|SIGINFO|SIGUSR1|SIGUSR2,0x0) = 0 (0x0)
sigprocmask(SIG_SETMASK,0x0,0x0)        = 0 (0x0)
open("(null)",O_NONBLOCK|O_DIRECTORY|O_CLOEXEC,037777756020) ERR#14 'Bad address

If I run my application sudo ./myls /<whatever_folder> I don't get the error message anymore, but nothing shows up. It seems like the input-stream is not working properly since what this application is supposed to do is to receive an argument as the param.<dir_name> and output a list with the content of that particular folder.

Questions:

1. What am I missing about this book example? Why do I get the fragmentation error?
2. What are the minimum header files necessary to create a simple CLI based application in FreeBSD, say for counting the total number of files in a folder? I've noticed that in the apue.h there are several system calls corresponding to multiple operating systems.
3. I'd like to be able to find a few examples to get started with C dev. in FreeBSD. What books/tutorials do you guys recommend ?
 
Hi,

I'm sure that naming the book that you are using will not infringe forums rules and is probably the best thing to do. It is possible that the book is out of date, poorly written or one to avoid for any other number of reasons.

1. What am I missing about this book example? Why do I get the fragmentation error?

Did you type the example in by hand? If so, you may want to check that you've entered it correctly. However, a good first step would be to run your code in a debugger and step through it to see where it fails and what the values of some of the variables are. The last line in your core dump output makes it look as though the open function is getting a NULL value for some reason. I would start by checking the value of the directory that you're passing in from within the running program and make sure that the variable dp is picking it up correctly.

3. I'd like to be able to find a few examples to get started with C dev. in FreeBSD.

You could try looking at the source for the ls command to see how it's currently written in FreeBSD.

What books/tutorials do you guys recommend ?

Unfortunately you don't state what your experience is with programming in general. Are you a complete novice? Or have you programmed in other languages before but not C? For a complete novice I would recommend a book along the lines of 'Head First C' published by O'Reilly. For a more experienced programmer I would recommend 'The C Programming Language' by Kernighan & Ritchie for the basics and historical aspect of the language and then 21st Century C (again, published by O'Reilly) to bring your skills up to the latest C11 standards. In between there are a few other books that are more intermediate like 'Programming in C' by Kochan or 'C Primer Plus' by Prata.

Hope that helps. Good luck!
 
See http://unix.stackexchange.com/questions/105483/compiling-code-from-apue

The apue.h file contains prototypes for a load of functions written to support the book (The file name is an acronym of the book title by the look of it). However you actually need to include the functions themselves when you build the executable. The Stack Exchange link above gives more detail.

In your program it looks like you're only really using the err_quit and err_sys functions from the libraries included with the book. You could drop the apue.h header and just use printf & exit in those two instances.

Also in your example it appears you have commented out the two apue function calls. This may cause problems with the if statements, as an if statement with no braces just applies to the next line of code. I'm not sure exactly how C handles bracket-less if statements but it might be that your while loop is only actually running if one or both of those error checks gets a match. It might be worth properly using brackets around your if statements until you are a bit more comfortable with the language.

This is what usually happens
Code:
if( some_check )
  printf("Check Matched");

printf("Code outside if statement");

I'm no C programmer, but this could be happening. (If it doesn't work like this, I would expect the build to error)
Code:
if( some_check )
  // commented out code

printf("This might now be linked to the if statement");

This is a lot clearer:
Code:
if( some_check ){
  // any code or comments (or nothing at all)
}

printf("This is completely outside if block");
 
usdmatt , dropping that header file fixed my problem. I used the
Code:
printf()
and
Code:
exit()
as you suggested and all is good. The reason why I added that header into my code was (besides following their example) because I wasn't sure if there where any system calls required to make my file compile (that were required specifically for the BSD OS). After looking at their definitions I realized that there wasn't anything in there for FreeBSD. Thank you for pointing that out. This brings me to a follow-up question: is there a way to easily see what is being included in a header file (a header inclusion) from an unrelated .c file without having to leave the editor ? I think I'm deviating from my initial question. I should probably start a new question about setting up a good development environment, perhaps a cross-compiling system.

xavi ,
You could try looking at the source for the ls command to see how it's currently written in FreeBSD.

How could I do that? Is there a fluid way I could check header files (while coding into a new file) ? Or, would I need a remote IDE setup for that?

Unfortunately you don't state what your experience is with programming in general. Are you a complete novice? Or have you programmed in other languages before but not C? For a complete novice I would recommend a book along the lines of 'Head First C' published by O'Reilly. For a more experienced programmer I would recommend 'The C Programming Language' by Kernighan & Ritchie for the basics and historical aspect of the language and then 21st Century C (again, published by O'Reilly) to bring your skills up to the latest C11 standards. In between there are a few other books that are more intermediate like 'Programming in C' by Kochan or 'C Primer Plus' by Prata.

Most of my development is done using OOP based languages (Java and Objective-C), but I do have a minimum understanding of procedural programming. What I'm trying to learn is bare-bone BSD C POSIX development and also get accustomed to the system calls required for this environment. So, I decided to get started with Advanced Programming in the UNIX Environment book. I was hoping they would have gotten me started with the minimum header files number necessary to create a simple application, and move slowly explaining when and how I should add other system calls. I will keep at it for now and hopefully in the upcoming chapters everything will be revealed. :)
 
You can't find a better book than that one and I, along with many here, always point to it as one to use.

I just woke up so I might be rambling about the wrong thing but:

I did a fair amount of C programming for FreeBSD web servers and only used the usual header files: stdio.h, stdlib.h along with a few others for networking, strings, etc. There should nothing different from any C code you find anywhere else.

As far as looking at the header source, that's in /usr/src/include/ .
 
Back
Top