1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 /**
19  * mlockall_agent is a simple VM Agent that allows to lock the address space of
20  * the process. This avoids the process' memory eviction under pressure.
21  *
22  * One example is when on the same machine you run the Region Server and
23  * some map-reduce tasks, some unused data in the region server may get swapped
24  * and this affects the region server performance.
25  *
26  * You can load the agent by adding it as a jvm option:
27  * export HBASE_REGIONSERVER_OPTS="-agentpath:./libmlockall_agent.so=user=hbase"
28  */
29 
30 #include <libgen.h>
31 #include <grp.h>
32 #include <pwd.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/mman.h>
37 #include <sys/resource.h>
38 #include <sys/time.h>
39 #include <sys/types.h>
40 #include <unistd.h>
41 #include "jvmti.h"
42 
43 typedef struct opts {
44   char *user;
45 } opts_t;
46 
47 #define PREFIX "mlockall_agent: "
48 #define LOG(format, ...) { fprintf(stderr, PREFIX format,##__VA_ARGS__); }
49 
parse_options(const char * options,opts_t * parsed)50 static int parse_options (const char *options, opts_t *parsed) {
51   char *optr, *opts_dup;
52   char *save2 = NULL;
53   char *save = NULL;
54   char *key, *val;
55   int ret = 0;
56   char *tok;
57 
58   memset(parsed, 0, sizeof(opts_t));
59   if (options == NULL)
60     return 0;  // No options means we'll just try outright
61   if ((opts_dup = strdup(options)) == NULL)
62     return(-1);
63 
64   optr = opts_dup;
65   while ((tok = strtok_r(optr, ",", &save)) != NULL) {
66     optr = NULL;
67     save2 = NULL;
68 
69     key = strtok_r(tok, "=", &save2);
70     val = strtok_r(NULL, "=", &save2);
71     if (!strcmp(key, "user")) {
72       parsed->user = strdup(val);
73     } else {
74       LOG("Unknown agent parameter '%s'\n", key);
75       ret = 1;
76     }
77   }
78 
79   free(opts_dup);
80   return(ret);
81 }
82 
warn_unless_root()83 static void warn_unless_root() {
84   if (geteuid() != 0) {
85     LOG("(this may be because java was not run as root!)\n");
86   }
87 }
88 
Agent_OnLoad(JavaVM * vm,char * options,void * reserved)89 JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {
90   struct passwd *pwd = NULL;
91   opts_t opts;
92 
93   if (parse_options(options, &opts)) {
94     return(1);
95   }
96 
97   // Check that this user exists
98   if (opts.user && (pwd = getpwnam(opts.user)) == NULL) {
99     LOG("Unable to setuid: could not find user '%s'\n", opts.user);
100     return(1);
101   }
102 
103   // Boost the mlock limit up to infinity
104   struct rlimit lim;
105   lim.rlim_max = RLIM_INFINITY;
106   lim.rlim_cur = lim.rlim_max;
107   if (setrlimit(RLIMIT_MEMLOCK, &lim)) {
108     perror(PREFIX "Unable to boost memlock resource limit");
109     warn_unless_root();
110     return(1);
111   }
112 
113   // Actually lock our memory, including future allocations.
114   if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
115     perror(PREFIX "Unable to lock memory.");
116     warn_unless_root();
117     return(1);
118   }
119 
120   LOG("Successfully locked memory\n");
121 
122   if (opts.user != NULL) {
123     // Drop down to the user's supplemental group list
124     if (initgroups(opts.user, pwd->pw_gid)) {
125       perror(PREFIX "Unable to initgroups");
126       warn_unless_root();
127       return(1);
128     }
129 
130     // And primary group ID
131     if (setgid(pwd->pw_gid)) {
132       perror(PREFIX "Unable to setgid");
133       warn_unless_root();
134       return(1);
135     }
136 
137     // And user ID
138     if (setuid(pwd->pw_uid)) {
139       perror(PREFIX "Unable to setuid");
140       warn_unless_root();
141       return(1);
142     }
143     LOG("Successful setuid to %s\n", opts.user);
144   }
145 
146   return(0);
147 }
148 
149