1 /*
   2  * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @modules java.base/jdk.internal.misc
  27  */
  28 
  29 import java.io.File;
  30 import java.io.FileOutputStream;
  31 import java.lang.invoke.MethodHandles;
  32 import java.nicl.*;
  33 import java.nicl.types.*;
  34 import java.nicl.metadata.*;
  35 
  36 public class UnixSystem {
  37     @NativeHeader
  38     static interface system {
  39         @NativeLocation(file="dummy", line=1, column=1, USR="c:@F@getpid")
  40         @NativeType(layout="()i", ctype="dummy")
  41         public abstract int getpid();
  42 
  43         @NativeLocation(file="dummy", line=1, column=1, USR="c:@F@snprintf")
  44         @NativeType(layout="(p:clp:c*)i", ctype="dummy")
  45         public abstract int snprintf(Pointer<Byte> buf, long size, Pointer<Byte> fmt, Object... args);
  46 
  47         @NativeLocation(file="dummy", line=1, column=1, USR="c:@F@strerror")
  48         @NativeType(layout="(i)p:c", ctype="dummy")
  49         public abstract Pointer<Byte> strerror(int errno);
  50 
  51         @NativeLocation(file="dummy", line=1, column=1, USR="c:@errno")
  52         @NativeType(layout="i", ctype="dummy")
  53         public abstract int errno$get();
  54 
  55         @NativeLocation(file="dummy", line=1, column=1, USR="c:@environ")
  56         @NativeType(layout="p:p:V", ctype="dummy", name="environ")
  57         public abstract Pointer<Pointer<Byte>> environ$get();
  58 
  59         public abstract Pointer<Pointer<Pointer<Byte>>> environ$ptr();
  60     }
  61 
  62     @NativeHeader
  63     static interface LinuxSystem {
  64         @NativeLocation(file="dummy", line=1, column=1, USR="c:@F@__xstat")
  65         @NativeType(layout="(ip:cp:[iiiiiiiiiiiii])i", ctype="dummy")
  66         public abstract int __xstat(int ver, Pointer<Byte> path, Pointer<stat> buf);
  67 
  68         @NativeLocation(file="dummy", line=47, column=11, USR="C:@S@MyStruct")
  69         @NativeStruct("[iiiiiiiiiiiii]")
  70         static interface stat extends Struct<stat> {
  71             @Offset(offset=384l)
  72             @NativeLocation(file="dummy", line=47, column=11, USR="c:@SA@stat@st_size")
  73             @NativeType(layout="i", ctype="off_t")
  74             int st_size$get();
  75             void st_size$set(int i);
  76         }
  77     }
  78 
  79     @NativeHeader
  80     static interface MacOSXSystem {
  81         @NativeLocation(file="dummy", line=1, column=1, USR="c:@F@stat")
  82         @NativeType(layout="(p:cp:[iSSQIIi[ll][ll][ll][ll]qqiIIi2q])i", ctype="dummy")
  83         public abstract int stat$INODE64(Pointer<Byte> path, Pointer<stat> buf);
  84 
  85 
  86         @NativeLocation(file="dummy", line=47, column=11, USR="C:@S@MyStruct")
  87         @NativeStruct("[iSSQIIi[ll][ll][ll][ll]qqiIIi2q]")
  88         static interface stat extends Struct<stat> {
  89             @Offset(offset=768l)
  90             @NativeLocation(file="dummy", line=47, column=11, USR="c:@SA@stat@st_size")
  91             @NativeType(layout="l", ctype="off_t")
  92             long st_size$get();
  93             void st_size$set(long i);
  94         }
  95     }
  96 
  97     private static final String OS = System.getProperty("os.name");
  98 
  99     public void testGetpid() {
 100         system i = Libraries.bind(MethodHandles.lookup(), system.class);
 101 
 102         long actual = i.getpid();
 103         long expected = ProcessHandle.current().pid();
 104 
 105         if (actual != expected) {
 106             throw new RuntimeException("Actual pid: " + actual + " does not match expected pid: " + expected);
 107         }
 108     }
 109 
 110     private static String lowerAndSprintf(system i, String fmt, Object... args) {
 111         System.err.println("lowerAndSprintf fmt=" + fmt);
 112         try (Scope scope = Scope.newNativeScope()) {
 113             long bufSize = 128;
 114 
 115             LayoutType<Byte> t = NativeTypes.UINT8;
 116             Pointer<Byte> buf = scope.allocate(t, bufSize);
 117             Pointer<Byte> cfmt = scope.toCString(fmt);
 118 
 119             int n = i.snprintf(buf, bufSize, cfmt, args);
 120             if (n >= bufSize) {
 121                 throw new IndexOutOfBoundsException(n);
 122             }
 123 
 124             return Pointer.toString(buf);
 125         }
 126     }
 127 
 128     public void testPrintf() {
 129         system i = Libraries.bind(MethodHandles.lookup(), system.class);
 130 
 131         int n;
 132 
 133         assertEquals("foo", lowerAndSprintf(i, "foo"));
 134         assertEquals("foo: 4711", lowerAndSprintf(i, "foo: %d", 4711));
 135         assertEquals("foo: 47 11", lowerAndSprintf(i, "foo: %d %d", 47, 11));
 136         try (Scope scope = Scope.newNativeScope()) {
 137             assertEquals("foo: bar", lowerAndSprintf(i, "foo: %s", scope.toCString("bar")));
 138             assertEquals("foo: bar baz", lowerAndSprintf(i, "foo: %s %s", scope.toCString("bar"), scope.toCString("baz")));
 139         }
 140     }
 141 
 142     private int getSizeUsingStat(String path) throws Exception {
 143         switch (OS) {
 144         case "Linux":
 145             return getSizeUsingStat_Linux(path);
 146         case "Mac OS X":
 147             return getSizeUsingStat_MacOSX(path);
 148         default:
 149             // FIXME: Add other operating systems here...
 150             throw new UnsupportedOperationException(OS + " not supported (yet)");
 151         }
 152     }
 153 
 154     private int getSizeUsingStat_Linux(String path) throws Exception {
 155         LinuxSystem i = Libraries.bind(MethodHandles.lookup(), LinuxSystem.class);
 156 
 157         try (Scope scope = Scope.newNativeScope()) {
 158             LinuxSystem.stat s = scope.allocateStruct(LinuxSystem.stat.class);
 159             Pointer<LinuxSystem.stat> p = s.ptr();
 160 
 161             s = p.get();
 162 
 163             int res = i.__xstat(1, scope.toCString(path), p);
 164             if (res != 0) {
 165                 throwErrnoException("Call to __xstat failed");
 166             }
 167 
 168             return s.st_size$get();
 169         }
 170     }
 171 
 172     private int getSizeUsingStat_MacOSX(String path) throws Exception {
 173         MacOSXSystem i = Libraries.bind(MethodHandles.lookup(), MacOSXSystem.class);
 174 
 175         try (Scope scope = Scope.newNativeScope()) {
 176             MacOSXSystem.stat s = scope.allocateStruct(MacOSXSystem.stat.class);
 177             Pointer<MacOSXSystem.stat> p = s.ptr();
 178 
 179             s = p.get();
 180 
 181             int res = i.stat$INODE64(scope.toCString(path), p);
 182             if (res != 0) {
 183                 throwErrnoException("Call to stat failed");
 184             }
 185 
 186             return (int)s.st_size$get();
 187         }
 188     }
 189 
 190     private static void throwErrnoException(String msg) {
 191         try {
 192             system sys = Libraries.bind(MethodHandles.lookup(), system.class);
 193             Pointer<Byte> p = sys.strerror(sys.errno$get());
 194             throw new Exception(msg + ": " + Pointer.toString(p));
 195         } catch (Throwable t) {
 196             throw new RuntimeException(t);
 197         }
 198     }
 199 
 200     public void testStat() {
 201         system i = Libraries.bind(MethodHandles.lookup(), system.class);
 202 
 203         int nBytes = 4711;
 204 
 205         try {
 206             File f = File.createTempFile("stat_test", null);
 207             FileOutputStream fos = new FileOutputStream(f);
 208             fos.write(new byte[nBytes]);
 209             fos.close();
 210 
 211             try {
 212                 assertEquals(nBytes, getSizeUsingStat(f.getPath()));
 213             } finally {
 214                 f.delete();
 215             }
 216         } catch (Exception e) {
 217             throw new RuntimeException(e);
 218         }
 219 
 220         try {
 221             int size = getSizeUsingStat("__surely_this___file_does_NOT_exist.txt");
 222             throw new RuntimeException("stat unexpectedly succeeded");
 223         } catch (Exception e) {
 224             // expected
 225         }
 226     }
 227 
 228 
 229     public void testEnviron() {
 230         system i = Libraries.bind(MethodHandles.lookup(), system.class);
 231 
 232         {
 233             // Pointer version
 234             Pointer<Pointer<Byte>> pp = i.environ$get().cast(NativeTypes.UINT8.pointer());
 235             Pointer<Byte> sp = pp.get();
 236             System.out.println("testEnviron.str: " + Pointer.toString(sp));
 237         }
 238 
 239         {
 240             // Reference version
 241             Pointer<Pointer<Pointer<Byte>>> r = i.environ$ptr();
 242             Pointer<Pointer<Byte>> spp = r.get().cast(NativeTypes.UINT8.pointer());
 243             Pointer<Byte> sp = spp.get().cast(NativeTypes.UINT8);
 244         }
 245     }
 246 
 247     private static void assertEquals(long expected, long actual) {
 248         if (expected != actual) {
 249             throw new RuntimeException("actual: " + actual + " does not match expected: " + expected);
 250         }
 251     }
 252 
 253     private static void assertEquals(String expected, String actual) {
 254         if (!expected.equals(actual)) {
 255             throw new RuntimeException("actual: " + actual + " does not match expected: " + expected);
 256         }
 257     }
 258 
 259     public static void main(String[] args) {
 260         UnixSystem us = new UnixSystem();
 261 
 262         us.testGetpid();
 263         us.testPrintf();
 264         us.testStat();
 265         us.testEnviron();
 266     }
 267 }