rpm  5.4.10
rpmjs.c
Go to the documentation of this file.
1 /* ***** BEGIN LICENSE BLOCK *****
2  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Mozilla Public License Version
5  * 1.1 (the "License"); you may not use this file except in compliance with
6  * the License. You may obtain a copy of the License at
7  * http://www.mozilla.org/MPL/
8  *
9  * Software distributed under the License is distributed on an "AS IS" basis,
10  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11  * for the specific language governing rights and limitations under the
12  * License.
13  *
14  * The Initial Developer of the Original Code is PageMail, Inc.
15  *
16  * Portions created by the Initial Developer are
17  * Copyright (c) 2007-2009, PageMail, Inc. All Rights Reserved.
18  *
19  * Contributor(s):
20  *
21  * Alternatively, the contents of this file may be used under the terms of
22  * either of the GNU General Public License Version 2 or later (the "GPL"),
23  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
24  * in which case the provisions of the GPL or the LGPL are applicable instead
25  * of those above. If you wish to allow use of your version of this file only
26  * under the terms of either the GPL or the LGPL, and not to allow others to
27  * use your version of this file under the terms of the MPL, indicate your
28  * decision by deleting the provisions above and replace them with the notice
29  * and other provisions required by the GPL or the LGPL. If you do not delete
30  * the provisions above, a recipient may use your version of this file under
31  * the terms of any one of the MPL, the GPL or the LGPL.
32  *
33  * ***** END LICENSE BLOCK *****
34  */
35 
36 #include "system.h"
37 
38 #include "rpmio_internal.h"
39 #include <argv.h>
40 #include <popt.h>
41 
42 #if defined(__APPLE__)
43 #include <crt_externs.h>
44 #else
45 extern char ** environ;
46 #endif
47 
48 #if defined(WITH_GPSEE)
49 
50 #define JS_THREADSAFE 1 /* XXX FIXME: current Fedora package broken. */
51 #define XP_UNIX 1 /* XXX needed? */
52 #undef DOXYGEN
53 #include <gpsee.h>
54 
55 typedef gpsee_interpreter_t * JSI_t;
56 #define _RPMJS_OPTIONS \
57  (JSOPTION_STRICT | JSOPTION_RELIMIT | JSOPTION_ANONFUNFIX | JSOPTION_JIT)
58 
59 #else /* WITH_GPSEE */
60 
61 typedef void * JSI_t;
62 #define _RPMJS_OPTIONS 0
63 
64 #endif /* WITH_GPSEE */
65 
66 #define _RPMJS_INTERNAL
67 #include "rpmjs.h"
68 
69 #include "debug.h"
70 
71 #define F_ISSET(_flags, _FLAG) ((_flags) & RPMJS_FLAGS_##_FLAG)
72 
73 /*@unchecked@*/
74 int _rpmjs_debug = 0;
75 
76 #define DBG(_t, _l) \
77  if ((_t) || _rpmjs_debug) fprintf _l
78 
79 /*@unchecked@*/ /*@relnull@*/
80 rpmjs _rpmjsI = NULL;
81 
82 /*@unchecked@*/
84 
85 /*@unchecked@*/
86 int _rpmjs_zeal = 2;
87 
88 struct rpmjs_s _rpmjs;
89 
90 struct poptOption rpmjsIPoptTable[] = {
91  { "allow", 'a', POPT_BIT_SET, &_rpmjs.flags, RPMJS_FLAGS_ALLOW,
92  N_("Allow (read-only) access to caller's environmen"), NULL },
93  { "nocache", 'C', POPT_BIT_SET, &_rpmjs.flags, RPMJS_FLAGS_NOCACHE,
94  N_("Disables compiler caching via JSScript XDR serialization"), NULL },
95  { "loadrc", 'R', POPT_BIT_SET, &_rpmjs.flags, RPMJS_FLAGS_LOADRC,
96  N_("Load RC file for interpreter based on script filename."), NULL },
97  { "nowarn", 'W', POPT_BIT_SET, &_rpmjs.flags, RPMJS_FLAGS_NOWARN,
98  N_("Do not report warnings"), NULL },
99 
100  { "norelimit", 'e', POPT_BIT_CLR, &_rpmjs.flags, RPMJS_FLAGS_RELIMIT,
101  N_("Do not limit regexps to n^3 levels of backtracking"), NULL },
102  { "nojit", 'J', POPT_BIT_CLR, &_rpmjs.flags, RPMJS_FLAGS_JIT,
103  N_("Disable nanojit"), NULL },
104  { "nostrict", 'S', POPT_BIT_CLR, &_rpmjs.flags, RPMJS_FLAGS_STRICT,
105  N_("Disable Strict mode"), NULL },
106  { "noutf8", 'U', POPT_BIT_SET, &_rpmjs.flags, RPMJS_FLAGS_NOUTF8,
107  N_("Disable UTF-8 C string processing"), NULL },
108  { "xml", 'x', POPT_BIT_SET, &_rpmjs.flags, RPMJS_FLAGS_XML,
109  N_("Parse <!-- comments --> as E4X tokens"), NULL },
110 
111  { "anonfunfix", '\0', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN, &_rpmjs.flags, RPMJS_FLAGS_ANONFUNFIX,
112  N_("Parse //@line number [\"filename\"] for XUL"), NULL },
113  { "atline", 'A', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN, &_rpmjs.flags, RPMJS_FLAGS_ATLINE,
114  N_("Parse //@line number [\"filename\"] for XUL"), NULL },
115  { "werror", 'w', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN, &_rpmjs.flags, RPMJS_FLAGS_WERROR,
116  N_("Convert warnings to errors"), NULL },
117 
118  POPT_TABLEEND
119 };
120 
121 static void rpmjsFini(void * _js)
122  /*@globals fileSystem @*/
123  /*@modifies *_js, fileSystem @*/
124 {
125  rpmjs js = _js;
126 
127 DBG(0, (stderr, "==> %s(%p) I %p\n", __FUNCTION__, js, js->I));
128 
129 #if defined(WITH_GPSEE)
130 #if defined(XXX_GPSEE_DEBUGGER)
131  gpsee_finiDebugger(js->jsdc);
132  js->jsdc = NULL;
133 #endif
134  (void) gpsee_destroyInterpreter(js->I);
135 #endif
136  js->I = NULL;
137 }
138 
139 /*@unchecked@*/ /*@only@*/ /*@null@*/
141 
142 static rpmjs rpmjsGetPool(/*@null@*/ rpmioPool pool)
143  /*@globals _rpmjsPool, fileSystem @*/
144  /*@modifies pool, _rpmjsPool, fileSystem @*/
145 {
146  rpmjs js;
147 
148  if (_rpmjsPool == NULL) {
149  _rpmjsPool = rpmioNewPool("js", sizeof(*js), -1, _rpmjs_debug,
150  NULL, NULL, rpmjsFini);
151  pool = _rpmjsPool;
152  }
153  return (rpmjs) rpmioGetPool(pool, sizeof(*js));
154 }
155 
156 static rpmjs rpmjsI(void)
157  /*@globals _rpmjsI @*/
158  /*@modifies _rpmjsI @*/
159 {
160  if (_rpmjsI == NULL) {
161 #if defined(WITH_GPSEE)
162  gpsee_verbosity(0); /* XXX hack around syslog(3) in GPSEE */
163 #endif
164  _rpmjsI = rpmjsNew(NULL, 0);
165  }
166 DBG(0, (stderr, "<== %s() _rpmjsI %p\n", __FUNCTION__, _rpmjsI));
167  return _rpmjsI;
168 }
169 
170 /* XXX FIXME: Iargv/Ienviron are now associated with running. */
171 rpmjs rpmjsNew(char ** av, uint32_t flags)
172 {
173  rpmjs js =
174 #ifdef NOTYET
175  (flags & 0x80000000) ? rpmjsI() :
176 #endif
177  rpmjsGetPool(_rpmjsPool);
178  JSI_t I = NULL;
179 
180 #if defined(WITH_GPSEE)
181 
182 #if defined(XXX_GPSEE_DEBUGGER) /* XXX js->jsdc? */
183  JSDContext *jsdc;
184 #endif
185 
186  if (flags == 0)
187  flags = _rpmjs_options;
188 
189  if (F_ISSET(flags, NOUTF8) || getenv("GPSEE_NO_UTF8_C_STRINGS")) {
190  JS_DestroyRuntime(JS_NewRuntime(1024));
191  putenv((char *) "GPSEE_NO_UTF8_C_STRINGS=1");
192  }
193 
194  /* XXX FIXME: js->Iargv/js->Ienviron for use by rpmjsRunFile() */
195  I = gpsee_createInterpreter();
196 #if defined(XXX_GPSEE_DEBUGGER)
197  js->jsdc = gpsee_initDebugger(I->cx, I->realm, DEBUGGER_JS);
198 #endif
199 
200 #ifdef NOTYET /* FIXME: dig out where NOCACHE has moved. */
201  if (F_ISSET(flags, NOCACHE))
202  I->useCompilerCache = 0;
203 #endif
204  if (F_ISSET(flags, NOWARN)) {
205  gpsee_runtime_t * grt = JS_GetRuntimePrivate(JS_GetRuntime(I->cx));
206  grt->errorReport |= er_noWarnings;
207  }
208 
209  JS_SetOptions(I->cx, (flags & 0xffff));
210 #if defined(JS_GC_ZEAL)
211  JS_SetGCZeal(I->cx, _rpmjs_zeal);
212 #endif
213 #endif /* WITH_GPSEE */
214 
215  js->flags = flags;
216  js->I = I;
217 
218  return rpmjsLink(js);
219 }
220 
221 #if defined(WITH_GPSEE)
222 static FILE * rpmjsOpenFile(rpmjs js, const char * fn, const char ** msgp)
223  /*@modifies js @*/
224 {
225  FILE * fp = NULL;
226 
227  fp = fopen(fn, "r");
228  if (fp == NULL || ferror(fp)) {
229  if (fp) {
230  if (msgp)
231  *msgp = xstrdup(strerror(errno));
232  (void) fclose(fp);
233  fp = NULL;
234  } else {
235  if (msgp) /* XXX FIXME: add __FUNCTION__ identifier? */
236  *msgp = xstrdup("unknown error");
237  }
238  goto exit;
239  }
240 
241  gpsee_flock(fileno(fp), GPSEE_LOCK_SH);
242 
243  if (F_ISSET(js->flags, SKIPSHEBANG)) {
244  char buf[BUFSIZ];
245 
246  if (fgets(buf, sizeof(buf), fp)) {
247  if (!(buf[0] == '#' && buf[1] == '!')) {
248  /* XXX FIXME: return through *msgp */
249  rpmlog(RPMLOG_WARNING, "%s: %s: no \'#!\' on 1st line\n",
250  __FUNCTION__, fn);
251  rewind(fp);
252  } else {
253 #ifdef NOTYET /* XXX FIXME */
254 gpsee_interpreter_t * I = js->I;
255  I->linenoOffset += 1;
256 #endif /* NOTYET */
257  do { /* consume entire first line, regardless of length */
258  if (strchr(buf, '\n'))
259  break;
260  } while (fgets(buf, sizeof(buf), fp));
261  /*
262  * Make spidermonkey think the script starts with a blank line,
263  * to keep line numbers in sync.
264  */
265  ungetc('\n', fp);
266  }
267  }
268  }
269 
270 exit:
271 
272 DBG(0, (stderr, "<== %s(%p,%s,%p) fp %p\n", __FUNCTION__, js, fn, msgp, fp));
273 
274  return fp;
275 }
276 
277 #ifdef NOTYET /* XXX FIXME */
278 static void processInlineFlags(rpmjs js, FILE * fp, signed int *verbosity_p)
279 {
280  char buf[256];
281  off_t offset;
282 
283  offset = ftello(fp);
284 
285  while (fgets(buf, sizeof(buf), fp)) {
286  char *s, *e;
287 
288  if ((buf[0] != '/') || (buf[1] != '/'))
289  break;
290 
291  for (s = buf + 2; *s == ' ' || *s == '\t'; s++);
292  if (strncmp(s, "gpsee:", 6) != 0)
293  continue;
294 
295  for (s = s + 6; *s == ' ' || *s == '\t'; s++);
296 
297  for (e = s; *e; e++) {
298  switch (*e) {
299  case '\r':
300  case '\n':
301  case '\t':
302  case ' ':
303  *e = '\0';
304  break;
305  }
306  }
307 
308  if (s[0])
309  processFlags(gsr, s, verbosity_p);
310  }
311 
312  fseeko(fp, offset, SEEK_SET);
313 }
314 #endif /* NOTYET */
315 #endif /* WITH_GPSEE */
316 
317 rpmRC rpmjsRunFile(rpmjs js, const char * fn,
318  char *const * Iargv,
319  const char ** resultp)
320 {
321  rpmRC rc = RPMRC_FAIL;
322 
323  if (js == NULL) js = rpmjsI();
324 
325  if (fn != NULL) {
326 #if defined(WITH_GPSEE)
327  gpsee_interpreter_t * I = js->I;
328  FILE * fp = rpmjsOpenFile(js, fn, resultp);
329 
330  if (fp == NULL) {
331  I->grt->exitType = et_execFailure;
332  /* XXX FIXME: strerror in *resultp */
333  goto exit;
334  }
335 
336 #ifdef NOTYET /* XXX FIXME */
337  processInlineFlags(js, fp, &verbosity);
338  gpsee_setVerbosity(verbosity);
339 #endif
340 
341  /* Just compile and exit? */
342  if (F_ISSET(js->flags, NOEXEC)) {
343  JSScript *script = NULL;
344  JSObject *scrobj = NULL;
345 
346  if (!gpsee_compileScript(I->cx, fn,
347  fp, NULL, &script, I->realm->globalObject, &scrobj))
348  {
349  I->grt->exitType = et_exception;
350  /* XXX FIXME: isatty(3) */
351  gpsee_reportUncaughtException(I->cx, JSVAL_NULL,
352  (gpsee_verbosity(0) >= GSR_FORCE_STACK_DUMP_VERBOSITY)
353  ||
354  ((gpsee_verbosity(0) >= GPSEE_ERROR_OUTPUT_VERBOSITY)
355  && isatty(STDERR_FILENO)));
356  } else {
357  I->grt->exitType = et_finished;
358  rc = RPMRC_OK;
359  }
360  } else {
361  char *const * Ienviron = NULL;
362 
363  if (F_ISSET(js->flags, ALLOW)) {
364 #if defined(__APPLE__)
365  Ienviron = (char *const *) _NSGetEnviron();
366 #else
367  Ienviron = environ;
368 #endif
369  }
370 
371  I->grt->exitType = et_execFailure;
372  if (gpsee_runProgramModule(I->cx, fn,
373  NULL, fp, Iargv, Ienviron) == JS_FALSE)
374  {
375  int code = gpsee_getExceptionExitCode(I->cx);
376  if (code >= 0) {
377  I->grt->exitType = et_requested;
378  I->grt->exitCode = code;
379  /* XXX FIXME: format and return code in *resultp. */
380  /* XXX hack tp get code into rc -> ec by negating */
381  rc = -code;
382  } else
383  if (JS_IsExceptionPending(I->cx)) {
384  /* XXX FIXME: isatty(3) */
385  gpsee_reportUncaughtException(I->cx, JSVAL_NULL,
386  (gpsee_verbosity(0) >= GSR_FORCE_STACK_DUMP_VERBOSITY)
387  ||
388  ((gpsee_verbosity(0) >= GPSEE_ERROR_OUTPUT_VERBOSITY)
389  && isatty(STDERR_FILENO)));
390  }
391  } else {
392  I->grt->exitType = et_finished;
393  rc = RPMRC_OK;
394  }
395  }
396  fclose(fp);
397  fp = NULL;
398 #endif /* WITH_GPSEE */
399  }
400 
401 #if defined(WITH_GPSEE)
402 exit:
403 #endif /* WITH_GPSEE */
404 
405 DBG(0, (stderr, "<== %s(%p,%s) rc %d |%s|\n", __FUNCTION__, js, fn, rc, (resultp ? *resultp :"")));
406 
407  return rc;
408 }
409 
410 rpmRC rpmjsRun(rpmjs js, const char * str, const char ** resultp)
411 {
412  rpmRC rc = RPMRC_FAIL;
413 
414  if (!(str && *str))
415  goto exit;
416 
417  if (js == NULL) js = rpmjsI();
418 
419 #if defined(WITH_GPSEE)
420  { gpsee_interpreter_t * I = js->I;
421  jsval v = JSVAL_VOID;
422  JSBool ok = JS_EvaluateScript(I->cx, I->realm->globalObject,
423  str, strlen(str), __FILE__, __LINE__, &v);
424  if (!ok)
425  goto exit;
426  rc = RPMRC_OK;
427  if (resultp && JSVAL_IS_STRING(v)) {
428  JSString * rstr = JSVAL_TO_STRING(v);
429  size_t ns = JS_GetStringEncodingLength(I->cx, rstr);
430  char * s = xmalloc(ns+1);
431  ns = JS_EncodeStringToBuffer(rstr, s, ns);
432  s[ns] = '\0';
433  *resultp = s;
434  }
435  }
436 #endif /* WITH_GPSEE */
437 
438 exit:
439 DBG(0, (stderr, "<== %s(%p,%p[%u]) rc %d\n", __FUNCTION__, js, str, (unsigned)(str ? strlen(str) : 0), rc));
440 
441  return rc;
442 }