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         @C(file="dummy", line=1, column=1, USR="c:@F@getpid")
  40         @NativeType(layout="()i", ctype="dummy", size=1)
  41         @CallingConvention(value=1)
  42         public abstract int getpid();
  43 
  44         @C(file="dummy", line=1, column=1, USR="c:@F@snprintf")
  45         @NativeType(layout="(p:clp:c*)i", ctype="dummy", size=1)
  46         @CallingConvention(value=1)
  47         public abstract int snprintf(Pointer<Byte> buf, long size, Pointer<Byte> fmt, Object... args);
  48 
  49         @C(file="dummy", line=1, column=1, USR="c:@F@strerror")
  50         @NativeType(layout="(i)p:c", ctype="dummy", size=1)
  51         @CallingConvention(value=1)
  52         public abstract Pointer<Byte> strerror(int errno);
  53 
  54         @C(file="dummy", line=1, column=1, USR="c:@errno")
  55         @NativeType(layout="i", ctype="dummy", size=4)
  56         public abstract int errno$get();
  57 
  58         @C(file="dummy", line=1, column=1, USR="c:@environ")
  59         @NativeType(layout="p:p:V", ctype="dummy", size=8, name="environ")
  60         public abstract Pointer<Pointer<Byte>> environ$get();
  61 
  62         public abstract Pointer<Pointer<Pointer<Byte>>> environ$ptr();
  63     }
  64 
  65     @NativeHeader
  66     static interface LinuxSystem {
  67         @C(file="dummy", line=1, column=1, USR="c:@F@__xstat")
  68         @NativeType(layout="(ip:cp:[iiiiiiiiiiiii])i", ctype="dummy", size=1)
  69         @CallingConvention(value=1)
  70         public abstract int __xstat(int ver, Pointer<Byte> path, Pointer<stat> buf);
  71 
  72         @NativeType(layout="[iiiiiiiiiiiii]", ctype="dummy", size=144, isRecordType=true)
  73         @C(file="dummy", line=47, column=11, USR="C:@S@MyStruct")
  74         static interface stat extends Struct<stat> {
  75             @Offset(offset=384l)
  76             @C(file="dummy", line=47, column=11, USR="c:@SA@stat@st_size")
  77             @NativeType(layout="i", ctype="off_t", size=4l)
  78             int st_size$get();
  79             void st_size$set(int i);
  80         }
  81     }
  82 
  83     @NativeHeader
  84     static interface MacOSXSystem {
  85         @C(file="dummy", line=1, column=1, USR="c:@F@stat")
  86         @NativeType(layout="(p:cp:[iSSQIIi[ll][ll][ll][ll]qqiIIi2q])i", ctype="dummy", size=1)
  87         @CallingConvention(value=1)
  88         public abstract int stat$INODE64(Pointer<Byte> path, Pointer<stat> buf);
  89 
  90 
  91         @NativeType(layout="[iSSQIIi[ll][ll][ll][ll]qqiIIi2q]", ctype="dummy", size=144, isRecordType=true)
  92         @C(file="dummy", line=47, column=11, USR="C:@S@MyStruct")
  93         static interface stat extends Struct<stat> {
  94             @Offset(offset=768l)
  95             @C(file="dummy", line=47, column=11, USR="c:@SA@stat@st_size")
  96             @NativeType(layout="l", ctype="off_t", size=4l)
  97             long st_size$get();
  98             void st_size$set(long i);
  99         }
 100     }
 101 
 102     private static final String OS = System.getProperty("os.name");
 103 
 104     public void testGetpid() {
 105         system i = Libraries.bind(MethodHandles.lookup(), system.class);
 106 
 107         long actual = i.getpid();
 108         long expected = ProcessHandle.current().pid();
 109 
 110         if (actual != expected) {
 111             throw new RuntimeException("Actual pid: " + actual + " does not match expected pid: " + expected);
 112         }
 113     }
 114 
 115     private static String lowerAndSprintf(system i, String fmt, Object... args) {
 116         System.err.println("lowerAndSprintf fmt=" + fmt);
 117         try (Scope scope = Scope.newNativeScope()) {
 118             long bufSize = 128;
 119 
 120             LayoutType<Byte> t = NativeTypes.UINT8;
 121             Pointer<Byte> buf = scope.allocate(t, bufSize);
 122             Pointer<Byte> cfmt = scope.toCString(fmt);
 123 
 124             int n = i.snprintf(buf, bufSize, cfmt, args);
 125             if (n >= bufSize) {
 126                 throw new IndexOutOfBoundsException(n);
 127             }
 128 
 129             return Pointer.toString(buf);
 130         }
 131     }
 132 
 133     public void testPrintf() {
 134         system i = Libraries.bind(MethodHandles.lookup(), system.class);
 135 
 136         int n;
 137 
 138         assertEquals("foo", lowerAndSprintf(i, "foo"));
 139         assertEquals("foo: 4711", lowerAndSprintf(i, "foo: %d", 4711));
 140         assertEquals("foo: 47 11", lowerAndSprintf(i, "foo: %d %d", 47, 11));
 141         try (Scope scope = Scope.newNativeScope()) {
 142             assertEquals("foo: bar", lowerAndSprintf(i, "foo: %s", scope.toCString("bar")));
 143             assertEquals("foo: bar baz", lowerAndSprintf(i, "foo: %s %s", scope.toCString("bar"), scope.toCString("baz")));
 144         }
 145     }
 146 
 147     private int getSizeUsingStat(String path) throws Exception {
 148         switch (OS) {
 149         case "Linux":
 150             return getSizeUsingStat_Linux(path);
 151         case "Mac OS X":
 152             return getSizeUsingStat_MacOSX(path);
 153         default:
 154             // FIXME: Add other operating systems here...
 155             throw new UnsupportedOperationException(OS + " not supported (yet)");
 156         }
 157     }
 158 
 159     private int getSizeUsingStat_Linux(String path) throws Exception {
 160         LinuxSystem i = Libraries.bind(MethodHandles.lookup(), LinuxSystem.class);
 161 
 162         try (Scope scope = Scope.newNativeScope()) {
 163             LinuxSystem.stat s = scope.allocateStruct(LinuxSystem.stat.class);
 164             Pointer<LinuxSystem.stat> p = s.ptr();
 165 
 166             s = p.get();
 167 
 168             int res = i.__xstat(1, scope.toCString(path), p);
 169             if (res != 0) {
 170                 throwErrnoException("Call to __xstat failed");
 171             }
 172 
 173             return s.st_size$get();
 174         }
 175     }
 176 
 177     private int getSizeUsingStat_MacOSX(String path) throws Exception {
 178         MacOSXSystem i = Libraries.bind(MethodHandles.lookup(), MacOSXSystem.class);
 179 
 180         try (Scope scope = Scope.newNativeScope()) {
 181             MacOSXSystem.stat s = scope.allocateStruct(MacOSXSystem.stat.class);
 182             Pointer<MacOSXSystem.stat> p = s.ptr();
 183 
 184             s = p.get();
 185 
 186             int res = i.stat$INODE64(scope.toCString(path), p);
 187             if (res != 0) {
 188                 throwErrnoException("Call to stat failed");
 189             }
 190 
 191             return (int)s.st_size$get();
 192         }
 193     }
 194 
 195     private static void throwErrnoException(String msg) {
 196         try {
 197             system sys = Libraries.bind(MethodHandles.lookup(), system.class);
 198             Pointer<Byte> p = sys.strerror(sys.errno$get());
 199             throw new Exception(msg + ": " + Pointer.toString(p));
 200         } catch (Throwable t) {
 201             throw new RuntimeException(t);
 202         }
 203     }
 204 
 205     public void testStat() {
 206         system i = Libraries.bind(MethodHandles.lookup(), system.class);
 207 
 208         int nBytes = 4711;
 209 
 210         try {
 211             File f = File.createTempFile("stat_test", null);
 212             FileOutputStream fos = new FileOutputStream(f);
 213             fos.write(new byte[nBytes]);
 214             fos.close();
 215 
 216             try {
 217                 assertEquals(nBytes, getSizeUsingStat(f.getPath()));
 218             } finally {
 219                 f.delete();
 220             }
 221         } catch (Exception e) {
 222             throw new RuntimeException(e);
 223         }
 224 
 225         try {
 226             int size = getSizeUsingStat("__surely_this___file_does_NOT_exist.txt");
 227             throw new RuntimeException("stat unexpectedly succeeded");
 228         } catch (Exception e) {
 229             // expected
 230         }
 231     }
 232 
 233 
 234     public void testEnviron() {
 235         system i = Libraries.bind(MethodHandles.lookup(), system.class);
 236 
 237         {
 238             // Pointer version
 239             Pointer<Pointer<Byte>> pp = i.environ$get().cast(NativeTypes.UINT8.pointer());
 240             Pointer<Byte> sp = pp.get();
 241             System.out.println("testEnviron.str: " + Pointer.toString(sp));
 242         }
 243 
 244         {
 245             // Reference version
 246             Pointer<Pointer<Pointer<Byte>>> r = i.environ$ptr();
 247             Pointer<Pointer<Byte>> spp = r.get().cast(NativeTypes.UINT8.pointer());
 248             Pointer<Byte> sp = spp.get().cast(NativeTypes.UINT8);
 249         }
 250     }
 251 
 252     private static void assertEquals(long expected, long actual) {
 253         if (expected != actual) {
 254             throw new RuntimeException("actual: " + actual + " does not match expected: " + expected);
 255         }
 256     }
 257 
 258     private static void assertEquals(String expected, String actual) {
 259         if (!expected.equals(actual)) {
 260             throw new RuntimeException("actual: " + actual + " does not match expected: " + expected);
 261         }
 262     }
 263 
 264     public static void main(String[] args) {
 265         UnixSystem us = new UnixSystem();
 266 
 267         us.testGetpid();
 268         us.testPrintf();
 269         us.testStat();
 270         us.testEnviron();
 271     }
 272 }
--- EOF ---