/* Path: dog.ee.lbl.gov!pasteur!agate!howland.reston.ans.net!usc!cs.utexas.edu!uunet!math.fu-berlin.de!west.West.Sun.COM!news2me.EBay.Sun.COM!jethro.Corp.Sun.COM!spam!scott From: scott@spam.Corp.Sun.COM (Scott McNealy) Newsgroups: comp.unix.solaris,alt.hackers Subject: front end for multiplexing a 1-user Solaris compiler license Date: 10 Mar 1993 16:27:41 GMT Organization: Sun Microsystems, Inc. Lines: 169 Message-ID: Reply-To: scott@spam.Corp.Sun.COM (Scott McNealy) Approved: scott@spam.Corp.Sun.COM (Scott McNealy) NNTP-Posting-Host: spam */ /* cc.c - front end for multiplexing a 1-user Solaris compiler license * * In Solaris 2.x, Sun has unbundled and licensed the compiler. The * license is floating, and per-user instead of per process. That is, if * you have one license, then any one user can run any number of compiles * at the same time, but a second user can't run any compiles at all. * * This program takes advantage of this somewhat unusual setup by running * all compiles as the same user. The program should be installed setuid * to root. When run, it changes a few things so that it looks like it is * some arbitrary user, and then runs the real Solaris compiler. * * As long as all users run this instead of using Solaris's cc directly, * everyone will be able to compile as much as they like with only a * single-user compiler license. * * Some technical notes: It has to be setuid to root instead of to the * arbitrary normal user because under System V only root can set the real * uid, and the compiler's license checker looks at the real uid. And * root itself can't be the user to do compiles, since the license checker * doesn't allow root to use the compiler at all! The program also makes * the working directory world-writable during the compile, and changes it * back afterwards. This is so that the objects or executables can be * written there by the designated user. However, if you tell cc to write * objects to another directory, maybe a subdirectory, it won't work. So, * either don't write into subdirectories, or manually make the destination * directory world-writable yourself. * * This program is probably of limited usefulness. Most people who need a * compiler under Solaris will just get gcc. However, I found myself in a * situation where we had a free 1-user copy of the Solaris compiler. Living * with the limitation was out of the question, and writing this program * was easier than installing gcc. Maybe in the next release, Sun will * go to a per-process license and this will stop working, but for now * it's useful. */ /* Plug in the location of the real compiler here. */ #define REALCC "/opt/SUNWspro/bin/cc" /* Plus in the user to compile things as here. Don't use root. */ #define CUSER "test" /* Now compile using the real compiler, with "cc cc.c -o cc" or the like. * Move the resulting cc executable to some public location, and as root * do "chown root cc ; chmod 4755 cc". That ought to do it. */ #include #include #include #include #include #include #include static struct stat sb; static void restore_dot_mode() { if (chmod(".", sb.st_mode) < 0) { perror("error resetting mode of working dir"); exit(1); } } static void clean_n_exit(status) int status; { setuid(getuid()); restore_dot_mode(); exit(status); } static void sig_handler(junk) int junk; { clean_n_exit(1); } void main(argc, argv) int argc; char *argv[]; { struct passwd *pwd; uid_t cuid; char *name; char user_env[200], logname_env[200]; pid_t f; int status; /* Translate username into uid. */ pwd = getpwnam(CUSER); if (pwd == (struct passwd*) 0) { fprintf(stderr, "%s: no such user\n", CUSER); exit(1); } cuid = pwd->pw_uid; /* Stat working dir in this process so we can restore later. */ if (stat(".", &sb) < 0) { perror("can't stat working dir"); exit(1); } /* Chmod working dir in a subprocess so that euid of root * doesn't get lost. */ f = fork(); switch (f) { case -1: perror("fork()"); exit(1); case 0: setuid(getuid()); if (chmod(".", sb.st_mode | 022) < 0) { perror("error setting mode of working dir"); exit(1); } exit(0); } while (wait(&status) != f ) ; if (WEXITSTATUS(status) != 0) exit(WEXITSTATUS(status)); /* Trap signals to restore working dir mode. */ signal(SIGHUP, sig_handler); signal(SIGINT, sig_handler); signal(SIGQUIT, sig_handler); signal(SIGTERM, sig_handler); /* Fix up environment variables just to be anal. */ sprintf(user_env, "USER=%s", CUSER); putenv(user_env); sprintf(logname_env, "LOGNAME=%s", CUSER); putenv(logname_env); /* Fork a process to run the real cc. */ f = vfork(); switch (f) { case -1: perror("fork()"); exit(1); case 0: /* Set a umask of zero so any files created will be * writable by the calling user. */ umask(0); /* Change uids to the designated user. */ setuid(cuid); /* Exec the real cc. */ argv[0] = REALCC; execv(REALCC, argv); perror(REALCC); exit(1); } while (wait(&status) != f ) ; /* Done. */ clean_n_exit(WEXITSTATUS(status)); } -----End of forwarded message-----