/*
 * Checkout the access checking.
 *
 * This should print only "everything worked.".
 *
 * This program will fail if run by root (since root has different access
 * privileges than a regular user).
 */

#include <stdio.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>

struct msgnode { long typ; char msg[100]; };

main()
{
    int msgkey = 0xdabdab;
    int rval, msqid;
    long typ;
    struct msqid_ds ds;
    struct msgnode msg;
    int errcnt = 0;

    msg.typ = 1;

    /*
     * Try to get rid of any old msq queue with the same key
     */

    msqid = msgget(msgkey, IPC_CREAT | 0000);
    if ( msqid > 0 ) {
	msgctl(msqid,IPC_RMID,NULL);
    }

    /******************************************************************/

    /*
     * Create a queue with no access and try to
     * access it in various ways.
     */

    msqid = msgget(msgkey, IPC_CREAT | 0000);
    if ( msqid < 0 ) {
	perror("msgget perm 0000 failed");
	exit(1);
    }

    /* Shouldn't be able to upgrade to read privileges. */

    if ( msgget(msgkey, IPC_CREAT | 0400) < 0 ) {
	if ( errno == EACCES ) {
	    printf("msgget 0400 got EACCES for perm 0000 (correct)\n");
	} else {
	    perror("wrong errno for msgget 0400 perm 0000");
	    errcnt += 1;
	}
    } else {
	fprintf(stderr,"msgget 0400 worked (should have failed) for msgsnd perm 0000\n");
	errcnt += 1;
    }

    /* Shouldn't be able to upgrade to write privileges. */

    if ( msgget(msgkey, IPC_CREAT | 0200) < 0 ) {
	if ( errno == EACCES ) {
	    printf("msgget 0200 got EACCES for perm 0000 (correct)\n");
	} else {
	    perror("wrong errno for msgget 0200 perm 0000");
	    errcnt += 1;
	}
    } else {
	fprintf(stderr,"msgget 0200 worked (should have failed) for msgsnd perm 0000\n");
	errcnt += 1;
    }

    /* Shouldn't be able to send messages to it. */

    if ( msgsnd(msqid,&msg,10,IPC_NOWAIT) < 0 ) {
	if ( errno == EACCES ) {
	    printf("msgsnd got EACCES for perm 0000 (correct)\n");
	} else {
	    perror("wrong errno for msgsnd perm 0000");
	    errcnt += 1;
	}
    } else {
	fprintf(stderr,"msgsnd worked (should have failed) for msgsnd perm 0000\n");
	errcnt += 1;
    }

    /* Shouldn't be able to receive messages from it. */

    if ( msgrcv(msqid,&msg,10,typ,IPC_NOWAIT) < 0 ) {
	if ( errno == EACCES ) {
	    printf("msgrcv got EACCES for perm 0000 (correct)\n");
	} else {
	    perror("wrong errno for msgrcv perm 0000");
	    errcnt += 1;
	}
    } else {
	fprintf(stderr,"msgrcv worked (should have failed) for msgrcv perm 0000\n");
	errcnt += 1;
    }

    /* Throw away the message queue */

    if ( msgctl(msqid,IPC_RMID,NULL) != 0 ) {
	perror("can't remove queue perm 0000");
	exit(1);		/* can't really recover from this. */
    }

    /******************************************************************/

    /*
     * Create a queue with read access and try to
     * access it in various ways.
     */

    msqid = msgget(msgkey, IPC_CREAT | 0400);
    if ( msqid < 0 ) {
	perror("msgget perm 0400 failed");
	exit(1);
    }

    /* Should be able to get id requesting no privileges. */

    if ( msgget(msgkey, IPC_CREAT | 0000) < 0 ) {
	perror("msgget 0000 perm 0400");
	errcnt += 1;
    } else {
	fprintf(stderr,"msgget 0000 perm 0400 worked (correct)\n");
    }

    /* Shouldn't be able to upgrade to write privileges. */

    if ( msgget(msgkey, IPC_CREAT | 0200) < 0 ) {
	if ( errno == EACCES ) {
	    printf("msgget 0200 got EACCES for perm 0400 (correct)\n");
	} else {
	    perror("wrong errno for msgget 0200 perm 0400");
	    errcnt += 1;
	}
    } else {
	fprintf(stderr,"msgget 0200 worked (should have failed) for msgsnd perm 0400\n");
	errcnt += 1;
    }

    /* Shouldn't be able to send messages to it. */

    if ( msgsnd(msqid,&msg,10,IPC_NOWAIT) < 0 ) {
	if ( errno == EACCES ) {
	    printf("msgsnd got EACCES for perm 0400 (correct)\n");
	} else {
	    perror("wrong errno for msgsnd perm 0400");
	    errcnt += 1;
	}
    } else {
	fprintf(stderr,"msgsnd worked (should have failed) for msgsnd perm 0400\n");
	errcnt += 1;
    }

    /* Should be able to try to receive messages from it. */

    if ( msgrcv(msqid,&msg,10,typ,IPC_NOWAIT) < 0 ) {
#ifdef ENOMSG
	if ( errno == ENOMSG ) {
	    printf("msgrcv got ENOMSG for perm 0400 (correct)\n");
#else
	if ( errno == EAGAIN ) {
	    printf("msgrcv got EAGAIN for perm 0400 (non-conforming but 'ok')\n");
#endif
	} else {
	    perror("wrong errno for msgrcv perm 0400");
	    errcnt += 1;
	}
    } else {
#ifdef ENOMSG
	fprintf(stderr,"msgrcv worked (should have failed with ENOMSG) for msgrcv perm 0400\n");
#else
	fprintf(stderr,"msgrcv worked (should have failed with EAGAIN) for msgrcv perm 0400\n");
#endif
	errcnt += 1;
    }

    /* Throw away the message queue */

    if ( msgctl(msqid,IPC_RMID,NULL) != 0 ) {
	perror("can't remove queue perm 0400");
	exit(1);		/* can't really recover from this. */
    }

    /******************************************************************/

    /*
     * Create a queue with write access and try to
     * access it in various ways.
     */

    msqid = msgget(msgkey, IPC_CREAT | 0200);
    if ( msqid < 0 ) {
	perror("msgget perm 0200 failed");
	exit(1);
    }

    /* Should be able to get id requesting no privileges. */

    if ( msgget(msgkey, IPC_CREAT | 0000) < 0 ) {
	perror("msgget 0000 perm 0200");
	errcnt += 1;
    } else {
	fprintf(stderr,"msgget 0000 perm 0200 worked (correct)\n");
    }

    /* Shouldn't be able to upgrade to read privileges. */

    if ( msgget(msgkey, IPC_CREAT | 0400) < 0 ) {
	if ( errno == EACCES ) {
	    printf("msgget 0400 got EACCES for perm 0200 (correct)\n");
	} else {
	    perror("wrong errno for msgget 0400 perm 0200");
	    errcnt += 1;
	}
    } else {
	fprintf(stderr,"msgget 0400 worked (should have failed) for msgsnd perm 0200\n");
	errcnt += 1;
    }

    /* Should be able to send messages to it. */

    if ( msgsnd(msqid,&msg,10,IPC_NOWAIT) < 0 ) {
	perror("msgsnd perm 0200");
	errcnt += 1;
    } else {
	fprintf(stderr,"msgsnd worked for perm 0200 (correct)\n");
    }

    /* Shouldn't be able to try to receive messages from it. */

    if ( msgrcv(msqid,&msg,10,typ,IPC_NOWAIT) < 0 ) {
	if ( errno == EACCES ) {
	    printf("msgrcv got EACCES for perm 0200 (correct)\n");
	} else {
	    perror("wrong errno for msgrcv perm 0200");
	    errcnt += 1;
	}
    } else {
	fprintf(stderr,"msgrcv worked (should have failed with EACCES) for msgrcv perm 0200\n");
	errcnt += 1;
    }

    /* Throw away the message queue */

    if ( msgctl(msqid,IPC_RMID,NULL) != 0 ) {
	perror("can't remove queue perm 0200");
	exit(1);		/* can't really recover from this. */
    }

    /******************************************************************/

    if ( errcnt == 0 ) {
	printf("everything worked.\n");
    } else {
	printf("%d error%s detected.\n",errcnt,errcnt == 1 ? "" : "s");
    }
    exit(0);
}
