1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 #include <errno.h>
  30 #include <time.h>
  31 #include <string.h>
  32 #include <strings.h>
  33 #include <sys/times.h>
  34 #include <sys/lx_syscall.h>
  35 #include <sys/lx_misc.h>
  36 
  37 /*
  38  * time() - This cannot be passthrough because on Linux a bad buffer will
  39  *          set errno to EFAULT, and on Solaris the failure mode is documented
  40  *          as "undefined."
  41  *
  42  *          (At present, Solaris' time(2) will segmentation fault, as the call
  43  *          is simply a libc wrapper atop the time() syscall that will
  44  *          dereference the passed  pointer if it is non-zero.)
  45  */
  46 int
  47 lx_time(uintptr_t p1)
  48 {
  49         time_t ret = time((time_t *)0);
  50 
  51         if ((ret == (time_t)-1) ||
  52             ((p1 != 0) && (uucopy(&ret, (time_t *)p1, sizeof (ret)) != 0)))
  53                 return (-errno);
  54 
  55         return (ret);
  56 }
  57 
  58 /*
  59  * times() - The Linux implementation avoids writing to NULL, while Solaris
  60  *           returns EFAULT.
  61  */
  62 int
  63 lx_times(uintptr_t p1)
  64 {
  65         clock_t ret;
  66         struct tms buf, *tp = (struct tms *)p1;
  67 
  68         ret = times(&buf);
  69 
  70         if ((ret == -1) ||
  71             ((tp != NULL) && uucopy((void *)&buf, tp, sizeof (buf)) != 0))
  72                 return (-errno);
  73 
  74         return ((ret == -1) ? -errno : ret);
  75 }
  76 
  77 /*
  78  * setitimer() - the Linux implementation can handle tv_usec values greater
  79  *               than 1,000,000 where Solaris would return EINVAL.
  80  *
  81  *               There's still an issue here where Linux can handle a
  82  *               tv_sec value greater than 100,000,000 but Solaris cannot,
  83  *               but that would also mean setting an interval timer to fire
  84  *               over _three years_ in the future so it's unlikely anything
  85  *               other than Linux test suites will trip over it.
  86  */
  87 int
  88 lx_setitimer(uintptr_t p1, uintptr_t p2, uintptr_t p3)
  89 {
  90         struct itimerval itv;
  91         struct itimerval *itp = (struct itimerval *)p2;
  92 
  93         if (itp != NULL) {
  94                 if (uucopy(itp, &itv, sizeof (itv)) != 0)
  95                         return (-errno);
  96 
  97                 /*
  98                  * Adjust any tv_usec fields >= 1,000,000 by adding any whole
  99                  * seconds so indicated to tv_sec and leaving tv_usec as the
 100                  * remainder.
 101                  */
 102                 if (itv.it_interval.tv_usec >= MICROSEC) {
 103                         itv.it_interval.tv_sec +=
 104                             itv.it_interval.tv_usec / MICROSEC;
 105 
 106                         itv.it_interval.tv_usec %= MICROSEC;
 107                 }
 108                 if (itv.it_value.tv_usec >= MICROSEC) {
 109                         itv.it_value.tv_sec +=
 110                             itv.it_value.tv_usec / MICROSEC;
 111 
 112                         itv.it_value.tv_usec %= MICROSEC;
 113                 }
 114 
 115                 itp = &itv;
 116         }
 117 
 118         return ((setitimer((int)p1, itp, (struct itimerval *)p3) != 0) ?
 119                 -errno : 0);
 120 }
 121 
 122 /*
 123  * NOTE: The Linux man pages state this structure is obsolete and is
 124  *       unsupported, so it is declared here for sizing purposes only.
 125  */
 126 struct lx_timezone {
 127         int tz_minuteswest;     /* minutes W of Greenwich */
 128         int tz_dsttime;         /* type of dst correction */
 129 };
 130 
 131 /*
 132  * lx_gettimeofday() and lx_settimeofday() are implemented here rather than
 133  * as pass-through calls to Solaris' libc due to the need to return EFAULT
 134  * for a bad buffer rather than die with a segmentation fault.
 135  */
 136 int
 137 lx_gettimeofday(uintptr_t p1, uintptr_t p2)
 138 {
 139         struct timeval tv;
 140         struct lx_timezone tz;
 141 
 142         bzero(&tz, sizeof (tz));
 143         (void) gettimeofday(&tv, NULL);
 144 
 145         if ((p1 != NULL) &&
 146             (uucopy(&tv, (struct timeval *)p1, sizeof (tv)) < 0))
 147                 return (-errno);
 148 
 149         /*
 150          * The Linux man page states use of the second parameter is obsolete,
 151          * but gettimeofday(2) should still return EFAULT if it is set
 152          * to a bad non-NULL pointer (sigh...)
 153          */
 154         if ((p2 != NULL) &&
 155             (uucopy(&tz, (struct lx_timezone *)p2, sizeof (tz)) < 0))
 156                 return (-errno);
 157 
 158         return (0);
 159 }
 160 
 161 int
 162 lx_settimeofday(uintptr_t p1, uintptr_t p2)
 163 {
 164         struct timeval tv;
 165         struct lx_timezone tz;
 166 
 167         if ((p1 != NULL) &&
 168             (uucopy((struct timeval *)p1, &tv, sizeof (tv)) < 0))
 169                 return (-errno);
 170 
 171         /*
 172          * The Linux man page states use of the second parameter is obsolete,
 173          * but settimeofday(2) should still return EFAULT if it is set
 174          * to a bad non-NULL pointer (sigh...)
 175          */
 176         if ((p2 != NULL) &&
 177             (uucopy((struct lx_timezone *)p2, &tz, sizeof (tz)) < 0))
 178                 return (-errno);
 179 
 180         if ((p1 != NULL) && (settimeofday(&tv, NULL) < 0))
 181                 return (-errno);
 182 
 183         return (0);
 184 }