// Copyright 2018 Tobias Klauser. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sysconf

import (
	
	
	
	
	
	

	
	
)

const (
	// CLK_TCK is a constant on Linux for all architectures except alpha and ia64.
	// See e.g.
	// https://git.musl-libc.org/cgit/musl/tree/src/conf/sysconf.c#n30
	// https://github.com/containerd/cgroups/pull/12
	// https://lore.kernel.org/lkml/agtlq6$iht$1@penguin.transmeta.com/
	_SYSTEM_CLK_TCK = 100
)

func readProcFsInt64( string,  int64) int64 {
	,  := ioutil.ReadFile()
	if  != nil {
		return 
	}
	,  := strconv.ParseInt(string([:len()-1]), 0, 64)
	if  != nil {
		return 
	}
	return 
}

// getMemPages computes mem*unit/os.Getpagesize(), but avoids overflowing int64.
func getMemPages( uint64,  uint32) int64 {
	 := os.Getpagesize()
	for  > 1 &&  > 1 {
		 >>= 1
		 >>= 1
	}
	 *= uint64()
	for  > 1 {
		 >>= 1
		 >>= 1
	}
	return int64()
}

func getPhysPages() int64 {
	var  unix.Sysinfo_t
	 := unix.Sysinfo(&)
	if  != nil {
		return int64(0)
	}
	return getMemPages(uint64(.Totalram), .Unit)
}

func getAvPhysPages() int64 {
	var  unix.Sysinfo_t
	 := unix.Sysinfo(&)
	if  != nil {
		return int64(0)
	}
	return getMemPages(uint64(.Freeram), .Unit)
}

func getNprocsSysfs() (int64, error) {
	,  := numcpus.GetOnline()
	return int64(), 
}

func getNprocsProcStat() (int64, error) {
	,  := os.Open("/proc/stat")
	if  != nil {
		return -1, 
	}
	defer .Close()

	 := int64(0)
	 := bufio.NewScanner()
	for .Scan() {
		if  := strings.TrimSpace(.Text()); strings.HasPrefix(, "cpu") {
			 := strings.SplitN(, " ", 2)
			,  := strconv.ParseInt([0][3:], 10, 64)
			if  == nil {
				++
			}
		} else {
			// The current format of /proc/stat has all the
			// cpu* lines at the beginning. Assume this
			// stays this way.
			break
		}
	}
	return , nil
}

func getNprocs() int64 {
	,  := getNprocsSysfs()
	if  == nil {
		return 
	}

	,  = getNprocsProcStat()
	if  == nil {
		return 
	}

	// default to the value determined at runtime startup if all else fails
	return int64(runtime.NumCPU())
}

func getNprocsConf() int64 {
	,  := numcpus.GetConfigured()
	if  == nil {
		return int64()
	}

	// TODO(tk): fall back to reading /proc/cpuinfo on legacy systems
	// without sysfs?

	return getNprocs()
}

func hasClock( int32) bool {
	var  unix.Timespec
	if  := unix.ClockGetres(, &);  != nil {
		return false
	}
	return true
}

func max(,  int64) int64 {
	if  >  {
		return 
	}
	return 
}

func sysconf( int) (int64, error) {
	switch  {
	case SC_AIO_LISTIO_MAX:
		return -1, nil
	case SC_AIO_MAX:
		return -1, nil
	case SC_AIO_PRIO_DELTA_MAX:
		return _AIO_PRIO_DELTA_MAX, nil
	case SC_ARG_MAX:
		 := int64(_POSIX_ARG_MAX)
		var  unix.Rlimit
		if  := unix.Getrlimit(unix.RLIMIT_STACK, &);  == nil {
			 = max(, int64(.Cur/4))
		}
		return , nil
	case SC_ATEXIT_MAX:
		return _INT_MAX, nil
	case SC_CHILD_MAX:
		 := int64(-1)
		var  unix.Rlimit
		if  := unix.Getrlimit(unix.RLIMIT_NPROC, &);  == nil && .Cur != unix.RLIM_INFINITY {
			 = int64(.Cur)
		}
		return , nil
	case SC_CLK_TCK:
		return _SYSTEM_CLK_TCK, nil
	case SC_DELAYTIMER_MAX:
		return _DELAYTIMER_MAX, nil
	case SC_GETGR_R_SIZE_MAX:
		return _NSS_BUFLEN_GROUP, nil
	case SC_GETPW_R_SIZE_MAX:
		return _NSS_BUFLEN_PASSWD, nil
	case SC_MQ_OPEN_MAX:
		return -1, nil
	case SC_MQ_PRIO_MAX:
		return _MQ_PRIO_MAX, nil
	case SC_NGROUPS_MAX:
		return readProcFsInt64("/proc/sys/kernel/ngroups_max", _NGROUPS_MAX), nil
	case SC_OPEN_MAX:
		 := int64(_OPEN_MAX)
		var  unix.Rlimit
		if  := unix.Getrlimit(unix.RLIMIT_NOFILE, &);  == nil {
			 = int64(.Cur)
		}
		return , nil
	case SC_RTSIG_MAX:
		return _RTSIG_MAX, nil
	case SC_SEM_NSEMS_MAX:
		return -1, nil
	case SC_SEM_VALUE_MAX:
		return _SEM_VALUE_MAX, nil
	case SC_SIGQUEUE_MAX:
		var  unix.Rlimit
		if  := unix.Getrlimit(unix.RLIMIT_SIGPENDING, &);  == nil {
			return int64(.Cur), nil
		}
		return readProcFsInt64("/proc/sys/kernel/rtsig-max", _POSIX_SIGQUEUE_MAX), nil
	case SC_STREAM_MAX:
		return _STREAM_MAX, nil
	case SC_THREAD_DESTRUCTOR_ITERATIONS:
		return _POSIX_THREAD_DESTRUCTOR_ITERATIONS, nil
	case SC_THREAD_KEYS_MAX:
		return _PTHREAD_KEYS_MAX, nil
	case SC_THREAD_PRIO_INHERIT:
		return _POSIX_THREAD_PRIO_INHERIT, nil
	case SC_THREAD_PRIO_PROTECT:
		return _POSIX_THREAD_PRIO_PROTECT, nil
	case SC_THREAD_STACK_MIN:
		return _PTHREAD_STACK_MIN, nil
	case SC_THREAD_THREADS_MAX:
		return -1, nil
	case SC_TIMER_MAX:
		return -1, nil
	case SC_TTY_NAME_MAX:
		return _TTY_NAME_MAX, nil
	case SC_TZNAME_MAX:
		return -1, nil

	case SC_CPUTIME:
		if hasClock(unix.CLOCK_PROCESS_CPUTIME_ID) {
			return _POSIX_VERSION, nil
		}
		return -1, nil
	case SC_MONOTONIC_CLOCK:
		if hasClock(unix.CLOCK_MONOTONIC) {
			return _POSIX_VERSION, nil
		}
		return -1, nil
	case SC_SAVED_IDS:
		return _POSIX_SAVED_IDS, nil
	case SC_SPAWN:
		return _POSIX_SPAWN, nil
	case SC_SPIN_LOCKS:
		return _POSIX_SPIN_LOCKS, nil
	case SC_SPORADIC_SERVER:
		return _POSIX_SPORADIC_SERVER, nil
	case SC_SYNCHRONIZED_IO:
		return _POSIX_SYNCHRONIZED_IO, nil
	case SC_THREAD_ATTR_STACKADDR:
		return _POSIX_THREAD_ATTR_STACKADDR, nil
	case SC_THREAD_ATTR_STACKSIZE:
		return _POSIX_THREAD_ATTR_STACKSIZE, nil
	case SC_THREAD_CPUTIME:
		if hasClock(unix.CLOCK_THREAD_CPUTIME_ID) {
			return _POSIX_VERSION, nil
		}
		return -1, nil
	case SC_THREAD_PRIORITY_SCHEDULING:
		return _POSIX_THREAD_PRIORITY_SCHEDULING, nil
	case SC_THREAD_PROCESS_SHARED:
		return _POSIX_THREAD_PROCESS_SHARED, nil
	case SC_THREAD_SAFE_FUNCTIONS:
		return _POSIX_THREAD_SAFE_FUNCTIONS, nil
	case SC_THREAD_SPORADIC_SERVER:
		return _POSIX_THREAD_SPORADIC_SERVER, nil
	case SC_TRACE:
		return _POSIX_TRACE, nil
	case SC_TRACE_EVENT_FILTER:
		return _POSIX_TRACE_EVENT_FILTER, nil
	case SC_TRACE_EVENT_NAME_MAX:
		return -1, nil
	case SC_TRACE_INHERIT:
		return _POSIX_TRACE_INHERIT, nil
	case SC_TRACE_LOG:
		return _POSIX_TRACE_LOG, nil
	case SC_TRACE_NAME_MAX:
		return -1, nil
	case SC_TRACE_SYS_MAX:
		return -1, nil
	case SC_TRACE_USER_EVENT_MAX:
		return -1, nil
	case SC_TYPED_MEMORY_OBJECTS:
		return _POSIX_TYPED_MEMORY_OBJECTS, nil

	case SC_V7_ILP32_OFF32:
		return _POSIX_V7_ILP32_OFF32, nil
	case SC_V7_ILP32_OFFBIG:
		return _POSIX_V7_ILP32_OFFBIG, nil
	case SC_V7_LP64_OFF64:
		return _POSIX_V7_LP64_OFF64, nil
	case SC_V7_LPBIG_OFFBIG:
		return _POSIX_V7_LPBIG_OFFBIG, nil

	case SC_V6_ILP32_OFF32:
		return _POSIX_V6_ILP32_OFF32, nil
	case SC_V6_ILP32_OFFBIG:
		return _POSIX_V6_ILP32_OFFBIG, nil
	case SC_V6_LP64_OFF64:
		return _POSIX_V6_LP64_OFF64, nil
	case SC_V6_LPBIG_OFFBIG:
		return _POSIX_V6_LPBIG_OFFBIG, nil

	case SC_2_C_VERSION:
		return _POSIX2_C_VERSION, nil
	case SC_2_CHAR_TERM:
		return _POSIX2_CHAR_TERM, nil
	case SC_2_PBS,
		SC_2_PBS_ACCOUNTING,
		SC_2_PBS_CHECKPOINT,
		SC_2_PBS_LOCATE,
		SC_2_PBS_MESSAGE,
		SC_2_PBS_TRACK:
		return -1, nil
	case SC_2_UPE:
		return -1, nil

	case SC_XOPEN_CRYPT:
		// removed in glibc 2.28
		return -1, nil
	case SC_XOPEN_ENH_I18N:
		return _XOPEN_ENH_I18N, nil
	case SC_XOPEN_REALTIME:
		return _XOPEN_REALTIME, nil
	case SC_XOPEN_REALTIME_THREADS:
		return _XOPEN_REALTIME_THREADS, nil
	case SC_XOPEN_SHM:
		return _XOPEN_SHM, nil
	case SC_XOPEN_STREAMS:
		return -1, nil
	case SC_XOPEN_UNIX:
		return _XOPEN_UNIX, nil
	case SC_XOPEN_VERSION:
		return _XOPEN_VERSION, nil
	case SC_XOPEN_XCU_VERSION:
		return _XOPEN_XCU_VERSION, nil

	case SC_PHYS_PAGES:
		return getPhysPages(), nil
	case SC_AVPHYS_PAGES:
		return getAvPhysPages(), nil
	case SC_NPROCESSORS_CONF:
		return getNprocsConf(), nil
	case SC_NPROCESSORS_ONLN:
		return getNprocs(), nil
	case SC_UIO_MAXIOV: // same as _SC_IOV_MAX
		return _UIO_MAXIOV, nil
	}

	return sysconfGeneric()
}