Bug 32807

Summary: which utility replacement in C
Product: Base System Reporter: Peter Sanchez <fut0n>
Component: binAssignee: freebsd-bugs (Nobody) <bugs>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: 4.4-STABLE   
Hardware: Any   
OS: Any   

Description Peter Sanchez 2001-12-13 17:40:00 UTC
Just a simple C program to replace the perl which utility that comes 
default at /usr/bin/which (/usr/src/usr.bin/which). Its slighly faster 
than the perl version.

Fix: 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>

#define NGROUPS 15

struct pathinfo
{
    char path[1024];
    struct pathinfo *next;
};

struct pathinfo *all = NULL;
struct passwd *pw;
uid_t uid;
int found = 1, gfail = 0;
int groups[NGROUPS + 1], ngroups = (NGROUPS + 1);

void
printusage(bin)
    char *bin;
{
    fprintf(stderr,"usage: %s [-a] [-s] program ...\n",bin);
    return;
}

int
file_exists(file)
    char *file;
{
    struct stat info;
    int check, i;

    check = stat(file,&info);
    if(check == -1)
        return check; /* file doesnt exist */

    if(S_ISDIR(info.st_mode))
        return -1; /* file is a directory */

    /* 
     * I did not use access() here cause of man page warnings that 
     * it is a potential security risk and shoule NEVER be used. Not 
     * quit sure on how it can be a security risk, but I worked around 
     * it anyways.
     */
    if(info.st_uid == uid && info.st_mode & S_IFREG && info.st_mode & S_IXUSR)
        return check; /* user executable */

    if(gfail)
    {
        if(info.st_gid == pw->pw_gid && info.st_mode & S_IFREG &&
           info.st_mode & S_IXGRP)
            return check; /* group executable */
    }
    else
    {
        for(i = 0; i < ngroups; i++)
        {
            if(info.st_gid == groups[i] && info.st_mode & S_IFREG &&
               info.st_mode & S_IXGRP)
                return check; /* group executable */
        }
    }
    
    if(info.st_mode & S_IFREG && info.st_mode & S_IXOTH)
        return check; /* other executable */
    else
        return -1;
}

void
losepath(void)
{
    struct pathinfo *tmp;

    while(all != NULL)
    {
        tmp = all->next;
        free(all);
        all = tmp;
    }
    return;
}

void
findpath(void)
{
    struct pathinfo *cur = NULL;
    char *userpath = getenv("PATH");
    int i, x;
    
    if(userpath == NULL)
        exit(1);

    all = (struct pathinfo *)malloc((unsigned)sizeof(struct pathinfo));
    if(all == NULL)
    {
        fprintf(stderr,"Out of memory, malloc() failed!\n");
        exit(1);
    }
    cur = all;

    for(i = 0, x = 0; i < strlen(userpath); i++)
    {
        if(userpath[i] == ':')
        {
            cur->path[x] = '\0';
            x = 0;
            cur->next = (struct pathinfo *)malloc((unsigned)sizeof(struct pathinfo));
            if(cur->next == NULL)
            {
                losepath();
                fprintf(stderr,"Out of memory, malloc() failed!\n");
                exit(1);
            }
            cur = cur->next;
        }
        else
            cur->path[x++] = userpath[i];
    }
    cur->path[x] = '\0';
    cur->next = NULL;
    cur = all;
    return;
}

void
findprog(prog, aflag, sflag)
    char *prog;
    int aflag;
    int sflag;
{
    struct pathinfo *tmp;
    char tmpbuf[2048];

    tmp = all;
    while(all != NULL)
    {
        if(strchr(prog,'/'))
            strncpy(tmpbuf,prog,2048);
        else
            snprintf(tmpbuf,2048,"%s/%s",all->path,prog);

        if(!file_exists(tmpbuf))
        {
            found = 0;
            if(sflag && aflag) ;
            else if(sflag && !aflag)
            {
                all = tmp;
                return;
            }
            else if(aflag && !sflag)
                printf("%s\n",tmpbuf);
            else
            {
                printf("%s\n",tmpbuf);
                all = tmp;
                return;
            }
        }
        all = all->next;
    }
    all = tmp;
    return;
}

int
main(argc, argv)
    int argc;
    char *argv[];
{
    char buf[1024];
    int aflag, sflag, pass, i;

    aflag = sflag = pass = 0;
    if(argc < 2)
        return 1;
       
    if(!strncmp(argv[1],"-h",2) || !strncmp(argv[1],"--h",3) || 
       !strcmp(argv[1],"?"))
    {
        printusage(argv[0]);
        return 1;
    }

    uid = getuid();
    pw = getpwuid(uid);
    if(getgrouplist(pw->pw_name,pw->pw_gid,groups,&ngroups) == -1)
        gfail = 1;

    findpath();
    for(i = 1; i < argc; i++)
    {
        if(!pass && *argv[i] == '-')
        {
            if(!strcmp(argv[i],"-a"))
                aflag = 1;
            else if(!strcmp(argv[i],"-s"))
                sflag = 1;
            else
            {
                printusage(argv[0]);
                return 1;
            }
            continue;
        }

        pass = 1;
        strncpy(buf,argv[i],1024);
        findprog(buf,aflag,sflag);      
    }

    losepath();
    return sflag ? found : 0;
}
How-To-Repeat: N/A
Comment 1 Peter Sanchez 2001-12-13 20:56:33 UTC
Now I feel like an idiot. I knew about that bug and simple forgot about
it. Here is the updated source. Sorry about that.

Peter

-----------------------------------------------------------------------

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>

#define NGROUPS 15

struct pathinfo
{
    char *path;
    struct pathinfo *next;
};

struct pathinfo *all = NULL;
struct passwd *pw;
uid_t uid;
int found = 1, gfail = 0;
int groups[NGROUPS + 1], ngroups = (NGROUPS + 1);

void
printusage(bin)
    char *bin;
{
    fprintf(stderr,"usage: %s [-a] [-s] program ...\n",bin);
    return;
}

int
file_exists(file)
    char *file;
{
    struct stat info;
    int check, i;

    check = stat(file,&info);
    if(check == -1)
        return check; /* file doesnt exist */

    if(S_ISDIR(info.st_mode))
        return -1; /* file is a directory */

    /* 
     * I did not use access() here cause of man page warnings that 
     * it is a potential security risk and shoule NEVER be used. Not 
     * quit sure on how it can be a security risk, but I worked around 
     * it anyways.
     */
    if(info.st_uid == uid && info.st_mode & S_IFREG && info.st_mode & S_IXUSR)
        return check; /* user executable */

    if(gfail)
    {
       if(info.st_gid == pw->pw_gid && info.st_mode & S_IFREG &&
           info.st_mode & S_IXGRP)
            return check; /* group executable */
    }
    else
    {
        for(i = 0; i < ngroups; i++)
        {
            if(info.st_gid == groups[i] && info.st_mode & S_IFREG &&
               info.st_mode & S_IXGRP)
                return check; /* group executable */
        }
    }
    
    if(info.st_mode & S_IFREG && info.st_mode & S_IXOTH)
        return check; /* other executable */
    else
        return -1;
}

void
losepath(void)
{
    struct pathinfo *tmp;

    while(all != NULL)
    {
        tmp = all->next;
        free(all);
        all = tmp;
    }
    return;
}

void
findpath(void)
{
    struct pathinfo *cur = NULL;
    char *userpath = getenv("PATH");
    int i, x;
    
    if(userpath == NULL)
        exit(1);

    all = (struct pathinfo *)malloc((unsigned)sizeof(struct pathinfo));
    if(all == NULL)
    {
        fprintf(stderr,"Out of memory, malloc() failed!\n");
        exit(1);
    }
    cur = all;

    while((cur->path = strsep(&userpath,":")) != NULL)
    {
        cur->next = (struct pathinfo *)malloc((unsigned)sizeof(struct pathinfo));
        if(cur->next == NULL)
        {
            losepath();
            fprintf(stderr,"Out of memory, malloc() failed!\n");
            exit(1);
        }
        cur = cur->next;
    }
    cur->next = NULL;
    cur = all;
    return;
}

void
findprog(prog, aflag, sflag)
    char *prog;
    int aflag;
    int sflag;
{
    struct pathinfo *tmp;
    char tmpbuf[2048];

    tmp = all;
    while(all != NULL)
    {
        if(strchr(prog,'/'))
            strncpy(tmpbuf,prog,2048);
        else
            snprintf(tmpbuf,2048,"%s/%s",all->path,prog);

        if(!file_exists(tmpbuf))
        {
            found = 0;
            if(sflag && aflag) ;
            else if(sflag && !aflag)
            {
                all = tmp;
                return;
            }
            else if(aflag && !sflag)
                printf("%s\n",tmpbuf);
            else
            {
                printf("%s\n",tmpbuf);
                all = tmp;
                return;
            }
        }
        all = all->next;
    }
    all = tmp;
    return;
}

int
main(argc, argv)
    int argc;
    char *argv[];
{
    char buf[1024];
    int aflag, sflag, pass, i;

    aflag = sflag = pass = 0;
    if(argc < 2)
        return 1;
       
    if(!strncmp(argv[1],"-h",2) || !strncmp(argv[1],"--h",3) || 
       !strcmp(argv[1],"?"))
    {
        printusage(argv[0]);
        return 1;
    }

    uid = getuid();
    pw = getpwuid(uid);
    if(getgrouplist(pw->pw_name,pw->pw_gid,groups,&ngroups) == -1)
        gfail = 1;

    findpath();
    for(i = 1; i < argc; i++)
    {
        if(!pass && *argv[i] == '-')
        {
            if(!strcmp(argv[i],"-a"))
                aflag = 1;
            else if(!strcmp(argv[i],"-s"))
                sflag = 1;
            else
            {
                printusage(argv[0]);
                return 1;
            }
            continue;
        }

        pass = 1;
        strncpy(buf,argv[i],1024);
        findprog(buf,aflag,sflag);      
    }

    losepath();
    return sflag ? found : 0;
}


On Thu, Dec 13, 2001 at 01:03:45PM -0500, David Hill wrote:
> 
> 
> -----Original Message-----
> From: owner-freebsd-bugs@FreeBSD.ORG
> [mailto:owner-freebsd-bugs@FreeBSD.ORG]On Behalf Of Peter Sanchez
> Sent: Thursday, December 13, 2001 12:39 PM
> To: freebsd-gnats-submit@FreeBSD.org
> Subject: bin/32807: which utility replacement in C
> 
> 
> 
> >Number:         32807
> >Category:       bin
> >Synopsis:       which utility replacement in C
> >Confidential:   no
> >Severity:       non-critical
> >Priority:       low
> >Responsible:    freebsd-bugs
> >State:          open
> >Quarter:        
> >Keywords:       
> >Date-Required:
> >Class:          change-request
> >Submitter-Id:   current-users
> >Arrival-Date:   Thu Dec 13 09:40:00 PST 2001
> >Closed-Date:
> >Last-Modified:
> >Originator:     Peter Sanchez
> >Release:        4.4-STABLE
> >Organization:
> >Environment:
> FreeBSD char.linuxforlesbians.org 4.4-STABLE FreeBSD 4.4-STABLE #0: Thu
> Nov 22 14:19:15 MST 2001
> root@char.linuxforlesbians.org:/usr/src/sys/compile/CHAR  i386
> >Description:
> Just a simple C program to replace the perl which utility that comes 
> default at /usr/bin/which (/usr/src/usr.bin/which). Its slighly faster 
> than the perl version.
> >How-To-Repeat:
> N/A
> >Fix:
> #include <stdio.h>
> #include <string.h>
> #include <stdlib.h>
> #include <unistd.h>
> #include <sys/types.h>
> #include <sys/stat.h>
> #include <pwd.h>
> 
> #define NGROUPS 15
> 
> struct pathinfo
> {
>     char path[1024];
>     struct pathinfo *next;
> };
> 
> struct pathinfo *all = NULL;
> struct passwd *pw;
> uid_t uid;
> int found = 1, gfail = 0;
> int groups[NGROUPS + 1], ngroups = (NGROUPS + 1);
> 
> void
> printusage(bin)
>     char *bin;
> {
>     fprintf(stderr,"usage: %s [-a] [-s] program ...\n",bin);
>     return;
> }
> 
> int
> file_exists(file)
>     char *file;
> {
>     struct stat info;
>     int check, i;
> 
>     check = stat(file,&info);
>     if(check == -1)
>         return check; /* file doesnt exist */
> 
>     if(S_ISDIR(info.st_mode))
>         return -1; /* file is a directory */
> 
>     /* 
>      * I did not use access() here cause of man page warnings that 
>      * it is a potential security risk and shoule NEVER be used. Not 
>      * quit sure on how it can be a security risk, but I worked around 
>      * it anyways.
>      */
>     if(info.st_uid == uid && info.st_mode & S_IFREG && info.st_mode &
> S_IXUSR)
>         return check; /* user executable */
> 
>     if(gfail)
>     {
>         if(info.st_gid == pw->pw_gid && info.st_mode & S_IFREG &&
>            info.st_mode & S_IXGRP)
>             return check; /* group executable */
>     }
>     else
>     {
>         for(i = 0; i < ngroups; i++)
>         {
>             if(info.st_gid == groups[i] && info.st_mode & S_IFREG &&
>                info.st_mode & S_IXGRP)
>                 return check; /* group executable */
>         }
>     }
>     
>     if(info.st_mode & S_IFREG && info.st_mode & S_IXOTH)
>         return check; /* other executable */
>     else
>         return -1;
> }
> 
> void
> losepath(void)
> {
>     struct pathinfo *tmp;
> 
>     while(all != NULL)
>     {
>         tmp = all->next;
>         free(all);
>         all = tmp;
>     }
>     return;
> }
> 
> void
> findpath(void)
> {
>     struct pathinfo *cur = NULL;
>     char *userpath = getenv("PATH");
>     int i, x;
>     
>     if(userpath == NULL)
>         exit(1);
> 
>     all = (struct pathinfo *)malloc((unsigned)sizeof(struct pathinfo));
>     if(all == NULL)
>     {
>         fprintf(stderr,"Out of memory, malloc() failed!\n");
>         exit(1);
>     }
>     cur = all;
> 
>     for(i = 0, x = 0; i < strlen(userpath); i++)
>     {
>         if(userpath[i] == ':')
>         {
>             cur->path[x] = '\0';
>             x = 0;
>             cur->next = (struct pathinfo
> *)malloc((unsigned)sizeof(struct pathinfo));
>             if(cur->next == NULL)
>             {
>                 losepath();
>                 fprintf(stderr,"Out of memory, malloc() failed!\n");
>                 exit(1);
>             }
>             cur = cur->next;
>         }
>         else
>             cur->path[x++] = userpath[i];
>     }
>     cur->path[x] = '\0';
>     cur->next = NULL;
>     cur = all;
>     return;
> }
> 
> void
> findprog(prog, aflag, sflag)
>     char *prog;
>     int aflag;
>     int sflag;
> {
>     struct pathinfo *tmp;
>     char tmpbuf[2048];
> 
>     tmp = all;
>     while(all != NULL)
>     {
>         if(strchr(prog,'/'))
>             strncpy(tmpbuf,prog,2048);
>         else
>             snprintf(tmpbuf,2048,"%s/%s",all->path,prog);
> 
>         if(!file_exists(tmpbuf))
>         {
>             found = 0;
>             if(sflag && aflag) ;
>             else if(sflag && !aflag)
>             {
>                 all = tmp;
>                 return;
>             }
>             else if(aflag && !sflag)
>                 printf("%s\n",tmpbuf);
>             else
>             {
>                 printf("%s\n",tmpbuf);
>                 all = tmp;
>                 return;
>             }
>         }
>         all = all->next;
>     }
>     all = tmp;
>     return;
> }
> 
> int
> main(argc, argv)
>     int argc;
>     char *argv[];
> {
>     char buf[1024];
>     int aflag, sflag, pass, i;
> 
>     aflag = sflag = pass = 0;
>     if(argc < 2)
>         return 1;
>        
>     if(!strncmp(argv[1],"-h",2) || !strncmp(argv[1],"--h",3) || 
>        !strcmp(argv[1],"?"))
>     {
>         printusage(argv[0]);
>         return 1;
>     }
> 
>     uid = getuid();
>     pw = getpwuid(uid);
>     if(getgrouplist(pw->pw_name,pw->pw_gid,groups,&ngroups) == -1)
>         gfail = 1;
> 
>     findpath();
>     for(i = 1; i < argc; i++)
>     {
>         if(!pass && *argv[i] == '-')
>         {
>             if(!strcmp(argv[i],"-a"))
>                 aflag = 1;
>             else if(!strcmp(argv[i],"-s"))
>                 sflag = 1;
>             else
>             {
>                 printusage(argv[0]);
>                 return 1;
>             }
>             continue;
>         }
> 
>         pass = 1;
>         strncpy(buf,argv[i],1024);
>         findprog(buf,aflag,sflag);      
>     }
> 
>     losepath();
>     return sflag ? found : 0;
> }
> >Release-Note:
> >Audit-Trail:
> >Unformatted:
> 
> To Unsubscribe: send mail to majordomo@FreeBSD.org
> with "unsubscribe freebsd-bugs" in the body of the message
> 
> ---
> 
> Needs a little work.  If my PATH env variable contains a "path" larger
> than 1024, this app seg faults.
> 
> $ export PATH=$PATH:`perl -e "print '/bin/','x' x 4048"` 
> $ ./which ls
> Segmentation fault (core dumped)
> 
> - David
> 
> ---
> Outgoing mail is certified Virus Free.
> Checked by AVG anti-virus system (http://www.grisoft.com).
> Version: 6.0.303 / Virus Database: 164 - Release Date: 11/24/2001
>  

-- 
Peter Sanchez, aka fut0n	|	"The ability to read is what
 - fut0n@linuxforlesbians.org	|	 distinguishes Unix users from
 - www.linuxforlesbians.org	|	 those of more popular platforms."
 - FreeBSD or DIE		|			- John Lasser
Comment 2 Sheldon Hearn 2001-12-30 12:58:58 UTC
On 15 Dec 2001 00:42:07 +0100, Dag-Erling Smorgrav wrote:

> Yes, much better!  Though the explicit "return" from void functions
> isn't necessary.

Since you've been driving the review process, will you don an asbestos
suit and use this one to kill the existing perl script? :-)

Note that there's a malloc() that isn't tested for failure, but it's
hidden in a strdup().  This is in findprog().

Ciao,
Sheldon.
Comment 3 Crist J. Clark freebsd_committer freebsd_triage 2002-01-01 21:51:43 UTC
State Changed
From-To: open->closed

As pointed out by Bruce Evans, the which(1) in CURRENT is already 
C.
Comment 4 Sheldon Hearn 2002-01-07 13:30:47 UTC
On Wed, 02 Jan 2002 00:51:36 +1100, Bruce Evans wrote:

> Er, FreeBSD will most likely keep using the version that it already has.
> 
>     RCS file: /home/ncvs/src/usr.bin/which/which.c,v
>     Working file: which.c
>     head: 1.2

Argh!

RELENG_4 still uses the perl script.  We should change that after
4.5-RELEASE.

Damn! :-(

Ciao,
Sheldon.