source: src-sh/pbi-manager/pbime/pbime.c @ a7429d5

releng/10.0releng/10.0.1releng/10.0.2releng/10.0.3
Last change on this file since a7429d5 was a7429d5, checked in by Kris Moore <kris@…>, 10 months ago

Add a cool new system callback function to PBIs on 10.x and beyond

This allows PBIs, which run in a container, to use the pbisyscmd, openwith
or xdg-open, and "talk" to applications on the outside of the container.

The use-case for this would be a application like Thunderbird, which may
want to use the "xdg-open" command to open a PDF file with something else
on the system.

It could be used by other apps, such as terminal emulators to run a shell on the system,
even though the GUI is run in the container

  • Property mode set to 100644
File size: 5.5 KB
Line 
1/*-
2 * Copyright (c) 2003 Mike Barcroft <mike@FreeBSD.org>
3 * Copyright (c) 2007 Bill Moran/Collaborative Fusion
4 * Copyright (c) 2014 Kris Moore/PC-BSD Software <kris@pcbsd.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 */
29
30#include <sys/param.h>
31#include <sys/jail.h>
32#include <sys/types.h>
33#include <sys/wait.h>
34
35#include <err.h>
36#include <errno.h>
37#include <login_cap.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <pwd.h>
41#include <unistd.h>
42#include <string.h>
43
44static void     usage(void);
45
46void ch_user(void);
47
48void ch_user(void)
49{
50        int jid, ngroups;
51        uid_t huid;
52        struct passwd *husername, *jusername;
53        gid_t groups[NGROUPS];
54        login_cap_t *lcap;
55
56        /* Get the current user ID and user name in the host system */
57        huid = getuid();
58        husername = getpwuid(huid);
59
60        /* Get the user name in the jail */
61        jusername = getpwuid(huid);
62        if (jusername == NULL || strcmp(husername->pw_name, jusername->pw_name) != 0)
63                err(1, "Username mapping failed");
64        lcap = login_getpwclass(jusername);
65        if (lcap == NULL) {
66          err(1, "getpwclass: %s", jusername->pw_name);
67        }
68        ngroups = NGROUPS;
69        if (getgrouplist(jusername->pw_name, jusername->pw_gid, groups, &ngroups) != 0) 
70                err(1, "getgrouplist: %s", jusername->pw_name);
71        if (setgroups(ngroups, groups) != 0)
72                err(1, "setgroups");
73        if (setgid(jusername->pw_gid) != 0)
74                err(1, "setgid");
75        if (setusercontext(lcap, jusername, jusername->pw_uid,
76            LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0)
77                err(1, "setusercontext");
78        login_close(lcap);
79}
80
81int
82main(int argc, char *argv[])
83{
84        char newchroot[2048];
85        char mountscript[4096];
86        char pipetemplate[] = "/tmp/.pbi-callback-XXXXXXXXXXXXXXXX";
87        char *pipedir;
88        char fifoin[MAXPATHLEN]; 
89        int argoffset;
90
91        pid_t listen_pid;
92        pid_t app_pid;
93        int listen_status;
94        int app_status;
95
96        /* Is this a request to unmount? */
97        if ( argc == 3 ) {
98                if ( strcmp(argv[1], "umount") == 0 ) {
99                        strcpy(mountscript, "/usr/pbi/.pbimount umount ");
100                        strcat(mountscript, argv[2]);
101                        return system(mountscript);
102                }
103        }
104
105        if (argc < 4)
106                usage();
107
108
109        /* Run the mount script */
110        if ( (strlen("/usr/pbi/.pbimount") + strlen(argv[1]) + strlen(argv[2])) > 4090)
111                err(1, "Max length exceeded for pbidir / mntdir");
112
113        if ( strlen("argv[2]") + strlen("/virtbase") > 2040)
114                err(1, "Max length exceeded for virtbase");
115        strcpy(newchroot, argv[1]);
116        strcat(newchroot, "/virtbase");
117       
118        strcpy(mountscript, "/usr/pbi/.pbimount ");
119        strcat(mountscript, argv[1]);
120        strcat(mountscript, " ");
121        strcat(mountscript, argv[2]);
122        //printf( "mountscript: %s \n", mountscript);
123        if ( system(mountscript) != 0 )
124                err(1, "Failed mounting PBI");
125
126        // Lets get our template directory
127        pipedir = mktemp(pipetemplate);
128
129        // Set the environment variable
130        setenv("PBI_SYSPIPE", pipedir, 1);
131       
132        // Fork off a listener for system commands
133        listen_pid = fork();
134        if(listen_pid == 0) {
135                /* This is done by the child process. */
136                // drop priv
137                ch_user();
138
139                // Create the pipe file
140                strcpy(fifoin, "mkfifo ");
141                strcat(fifoin, pipedir);
142                if ( system(fifoin) == 0 ) {
143                        // Start the listen process only if mkfifo is successful
144                        exit(system("sh /usr/pbi/.pbisyslisten"));
145                }
146        } else {
147
148                // Do the chroot
149                if (chdir(newchroot) == -1 || chroot(".") == -1)
150                        err(1, "Could not chroot to: %s", newchroot);
151
152                // drop priv
153                ch_user();
154
155                // Backwards compat check for old PBIs
156                argoffset=4;
157                if (chdir(argv[4]) == -1 ) {
158                        // Running with old pbi wrapper
159                        argoffset=3;
160                }
161
162
163                // Fork off the PBI process, and have the parent wait for it to finish
164                app_pid = fork();
165                if(app_pid == 0) {
166                        // Execute the PBI now
167                        if (execvp(argv[3], argv + argoffset) == -1)
168                                err(1, "execvp(): %s", argv[3]);
169                        exit(0);
170                } else {
171                        /* This is run by the parent.  Wait for the child to terminate. */
172                        pid_t tpid;
173                        do {
174                                tpid = wait(&app_status);
175                        } while(tpid != app_pid);
176
177                        // Stop the listener
178                        strcpy(fifoin, "echo 'CLOSE:' > ");
179                        strcat(fifoin, pipedir);
180                        system(fifoin);
181
182                        // Remove the old pipefile
183                        strcpy(fifoin, "rm ");
184                        strcat(fifoin, pipedir);
185                        system(fifoin);
186
187                        return app_status;
188                }
189
190        }
191
192        exit(0);
193}
194
195static void
196usage(void)
197{
198
199        fprintf(stderr, "usage: pbime mntdir pbidir command cwd [...]\n");
200        exit(1); 
201}
Note: See TracBrowser for help on using the repository browser.