2014-04-11 02:20:58 +08:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2017-05-29 15:17:15 +08:00
// Copyright 2017 The Gitea Authors. All rights reserved.
2022-11-28 02:20:29 +08:00
// SPDX-License-Identifier: MIT
2014-04-11 02:20:58 +08:00
2014-05-26 08:11:25 +08:00
package setting
2014-04-11 02:20:58 +08:00
import (
2023-02-04 01:22:11 +08:00
"errors"
2019-04-02 15:48:31 +08:00
"fmt"
2014-04-11 02:20:58 +08:00
"os"
"os/exec"
"path"
"path/filepath"
2019-04-29 03:48:46 +08:00
"runtime"
2016-08-12 05:46:33 +08:00
"strconv"
2014-04-11 02:20:58 +08:00
"strings"
2014-07-25 04:31:59 +08:00
"time"
2014-04-11 02:20:58 +08:00
2016-12-23 02:12:23 +08:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/user"
2020-11-28 10:42:08 +08:00
"code.gitea.io/gitea/modules/util"
2017-01-24 06:44:23 +08:00
2019-01-10 01:22:57 +08:00
ini "gopkg.in/ini.v1"
2014-04-11 02:20:58 +08:00
)
2016-11-27 18:14:25 +08:00
// settings
2014-04-11 02:20:58 +08:00
var (
2021-02-20 05:36:43 +08:00
// AppVer is the version of the current build of Gitea. It is set in main.go from main.Version.
AppVer string
// AppBuiltWith represents a human readable version go runtime build version and build tags. (See main.go formatBuiltWith().)
AppBuiltWith string
// AppStartTime store time gitea has started
AppStartTime time . Time
2023-02-20 00:12:01 +08:00
2021-02-20 05:36:43 +08:00
// AppPath represents the path to the gitea binary
AppPath string
// AppWorkPath is the "working directory" of Gitea. It maps to the environment variable GITEA_WORK_DIR.
// If that is not set it is the default set here by the linker or failing that the directory of AppPath.
//
// AppWorkPath is used as the base path for several other paths.
AppWorkPath string
2015-03-23 22:19:19 +08:00
2015-12-18 11:31:34 +08:00
// Global setting objects
2023-02-20 00:12:01 +08:00
CfgProvider ConfigProvider
CustomPath string // Custom directory path
CustomConf string
PIDFile = "/run/gitea.pid"
WritePIDFile bool
RunMode string
RunUser string
IsProd bool
IsWindows bool
2014-04-11 02:20:58 +08:00
)
2017-11-03 16:56:20 +08:00
func getAppPath ( ) ( string , error ) {
var appPath string
var err error
if IsWindows && filepath . IsAbs ( os . Args [ 0 ] ) {
appPath = filepath . Clean ( os . Args [ 0 ] )
} else {
appPath , err = exec . LookPath ( os . Args [ 0 ] )
2017-09-12 20:27:44 +08:00
}
2017-11-03 16:56:20 +08:00
2022-12-14 14:15:11 +08:00
if err != nil {
2023-02-04 01:22:11 +08:00
if ! errors . Is ( err , exec . ErrDot ) {
2022-12-14 14:15:11 +08:00
return "" , err
}
appPath , err = filepath . Abs ( os . Args [ 0 ] )
}
2014-05-26 08:11:25 +08:00
if err != nil {
return "" , err
}
2017-11-03 16:56:20 +08:00
appPath , err = filepath . Abs ( appPath )
if err != nil {
return "" , err
}
// Note: we don't use path.Dir here because it does not handle case
// which path starts with two "/" in Windows: "//psf/Home/..."
2020-10-12 04:27:20 +08:00
return strings . ReplaceAll ( appPath , "\\" , "/" ) , err
2017-11-03 16:56:20 +08:00
}
func getWorkPath ( appPath string ) string {
2019-04-30 02:08:21 +08:00
workPath := AppWorkPath
2017-11-03 16:56:20 +08:00
2019-04-30 02:08:21 +08:00
if giteaWorkPath , ok := os . LookupEnv ( "GITEA_WORK_DIR" ) ; ok {
2017-11-03 16:56:20 +08:00
workPath = giteaWorkPath
2019-04-30 02:08:21 +08:00
}
if len ( workPath ) == 0 {
2017-11-03 16:56:20 +08:00
i := strings . LastIndex ( appPath , "/" )
if i == - 1 {
workPath = appPath
} else {
workPath = appPath [ : i ]
}
}
2022-06-06 22:43:17 +08:00
workPath = strings . ReplaceAll ( workPath , "\\" , "/" )
if ! filepath . IsAbs ( workPath ) {
log . Info ( "Provided work path %s is not absolute - will be made absolute against the current working directory" , workPath )
absPath , err := filepath . Abs ( workPath )
if err != nil {
log . Error ( "Unable to absolute %s against the current working directory %v. Will absolute against the AppPath %s" , workPath , err , appPath )
workPath = filepath . Join ( appPath , workPath )
} else {
workPath = absPath
}
}
2020-10-12 04:27:20 +08:00
return strings . ReplaceAll ( workPath , "\\" , "/" )
2015-11-09 05:59:56 +08:00
}
func init ( ) {
2019-04-29 03:48:46 +08:00
IsWindows = runtime . GOOS == "windows"
2019-04-02 15:48:31 +08:00
// We can rely on log.CanColorStdout being set properly because modules/log/console_windows.go comes before modules/setting/setting.go lexicographically
2021-06-27 08:56:58 +08:00
// By default set this logger at Info - we'll change it later but we need to start with something.
log . NewLogger ( 0 , "console" , "console" , fmt . Sprintf ( ` { "level": "info", "colorize": %t, "stacktraceLevel": "none"} ` , log . CanColorStdout ) )
2015-11-09 05:59:56 +08:00
var err error
2017-11-03 16:56:20 +08:00
if AppPath , err = getAppPath ( ) ; err != nil {
2019-04-02 15:48:31 +08:00
log . Fatal ( "Failed to get app path: %v" , err )
2014-05-26 08:11:25 +08:00
}
2017-11-03 16:56:20 +08:00
AppWorkPath = getWorkPath ( AppPath )
2014-05-26 08:11:25 +08:00
}
2015-03-18 16:25:55 +08:00
func forcePathSeparator ( path string ) {
if strings . Contains ( path , "\\" ) {
2019-04-02 15:48:31 +08:00
log . Fatal ( "Do not use '\\' or '\\\\' in paths, instead, please use '/' in all places" )
2015-03-18 16:25:55 +08:00
}
}
2016-08-10 08:41:18 +08:00
// IsRunUserMatchCurrentUser returns false if configured run user does not match
// actual user that runs the app. The first return value is the actual user name.
// This check is ignored under Windows since SSH remote login is not the main
// method to login on Windows.
func IsRunUserMatchCurrentUser ( runUser string ) ( string , bool ) {
2019-06-16 10:49:07 +08:00
if IsWindows || SSH . StartBuiltinServer {
2016-08-10 08:41:18 +08:00
return "" , true
}
currentUser := user . CurrentUsername ( )
return currentUser , runUser == currentUser
}
2017-01-09 19:54:57 +08:00
func createPIDFile ( pidPath string ) {
currentPid := os . Getpid ( )
if err := os . MkdirAll ( filepath . Dir ( pidPath ) , os . ModePerm ) ; err != nil {
2019-04-02 15:48:31 +08:00
log . Fatal ( "Failed to create PID folder: %v" , err )
2017-01-09 19:54:57 +08:00
}
file , err := os . Create ( pidPath )
if err != nil {
2019-04-02 15:48:31 +08:00
log . Fatal ( "Failed to create PID file: %v" , err )
2017-01-09 19:54:57 +08:00
}
defer file . Close ( )
if _ , err := file . WriteString ( strconv . FormatInt ( int64 ( currentPid ) , 10 ) ) ; err != nil {
2019-04-02 15:48:31 +08:00
log . Fatal ( "Failed to write PID information: %v" , err )
2017-01-09 19:54:57 +08:00
}
}
2019-04-30 02:08:21 +08:00
// SetCustomPathAndConf will set CustomPath and CustomConf with reference to the
// GITEA_CUSTOM environment variable and with provided overrides before stepping
// back to the default
2019-05-14 23:20:35 +08:00
func SetCustomPathAndConf ( providedCustom , providedConf , providedWorkPath string ) {
if len ( providedWorkPath ) != 0 {
AppWorkPath = filepath . ToSlash ( providedWorkPath )
}
2019-04-30 02:08:21 +08:00
if giteaCustom , ok := os . LookupEnv ( "GITEA_CUSTOM" ) ; ok {
CustomPath = giteaCustom
}
if len ( providedCustom ) != 0 {
CustomPath = providedCustom
}
2014-05-26 08:11:25 +08:00
if len ( CustomPath ) == 0 {
2017-11-03 16:56:20 +08:00
CustomPath = path . Join ( AppWorkPath , "custom" )
} else if ! filepath . IsAbs ( CustomPath ) {
CustomPath = path . Join ( AppWorkPath , CustomPath )
2014-05-26 08:11:25 +08:00
}
2019-04-30 02:08:21 +08:00
if len ( providedConf ) != 0 {
CustomConf = providedConf
2017-01-09 19:54:57 +08:00
}
2015-02-05 18:12:37 +08:00
if len ( CustomConf ) == 0 {
2017-11-03 16:56:20 +08:00
CustomConf = path . Join ( CustomPath , "conf/app.ini" )
2017-07-01 11:10:04 +08:00
} else if ! filepath . IsAbs ( CustomConf ) {
2017-11-03 16:56:20 +08:00
CustomConf = path . Join ( CustomPath , CustomConf )
2020-02-03 00:20:20 +08:00
log . Warn ( "Using 'custom' directory as relative origin for configuration file: '%s'" , CustomConf )
2015-02-05 18:12:37 +08:00
}
2019-04-30 02:08:21 +08:00
}
2023-02-20 00:12:01 +08:00
// PrepareAppDataPath creates app data directory if necessary
func PrepareAppDataPath ( ) error {
// FIXME: There are too many calls to MkdirAll in old code. It is incorrect.
// For example, if someDir=/mnt/vol1/gitea-home/data, if the mount point /mnt/vol1 is not mounted when Gitea runs,
// then gitea will make new empty directories in /mnt/vol1, all are stored in the root filesystem.
// The correct behavior should be: creating parent directories is end users' duty. We only create sub-directories in existing parent directories.
// For quickstart, the parent directories should be created automatically for first startup (eg: a flag or a check of INSTALL_LOCK).
// Now we can take the first step to do correctly (using Mkdir) in other packages, and prepare the AppDataPath here, then make a refactor in future.
st , err := os . Stat ( AppDataPath )
if os . IsNotExist ( err ) {
err = os . MkdirAll ( AppDataPath , os . ModePerm )
if err != nil {
return fmt . Errorf ( "unable to create the APP_DATA_PATH directory: %q, Error: %w" , AppDataPath , err )
}
return nil
}
2021-12-01 15:50:01 +08:00
2023-02-20 00:12:01 +08:00
if err != nil {
return fmt . Errorf ( "unable to use APP_DATA_PATH %q. Error: %w" , AppDataPath , err )
}
2021-12-01 15:50:01 +08:00
2023-02-20 00:12:01 +08:00
if ! st . IsDir ( ) /* also works for symlink */ {
return fmt . Errorf ( "the APP_DATA_PATH %q is not a directory (or symlink to a directory) and can't be used" , AppDataPath )
2021-12-01 15:50:01 +08:00
}
2023-02-20 00:12:01 +08:00
return nil
2021-12-01 15:50:01 +08:00
}
2023-02-20 00:12:01 +08:00
// InitProviderFromExistingFile initializes config provider from an existing config file (app.ini)
func InitProviderFromExistingFile ( ) {
CfgProvider = newFileProviderFromConf ( CustomConf , WritePIDFile , false , PIDFile , "" )
2022-01-21 01:00:38 +08:00
}
2023-02-20 00:12:01 +08:00
// InitProviderAllowEmpty initializes config provider from file, it's also fine that if the config file (app.ini) doesn't exist
func InitProviderAllowEmpty ( ) {
CfgProvider = newFileProviderFromConf ( CustomConf , WritePIDFile , true , PIDFile , "" )
}
// InitProviderAndLoadCommonSettingsForTest initializes config provider and load common setttings for tests
func InitProviderAndLoadCommonSettingsForTest ( extraConfigs ... string ) {
CfgProvider = newFileProviderFromConf ( CustomConf , WritePIDFile , true , PIDFile , strings . Join ( extraConfigs , "\n" ) )
loadCommonSettingsFrom ( CfgProvider )
if err := PrepareAppDataPath ( ) ; err != nil {
log . Fatal ( "Can not prepare APP_DATA_PATH: %v" , err )
2022-10-17 07:29:26 +08:00
}
}
2023-02-20 00:12:01 +08:00
// newFileProviderFromConf initializes configuration context.
2019-04-30 02:08:21 +08:00
// NOTE: do not print any log except error.
2023-02-20 00:12:01 +08:00
func newFileProviderFromConf ( customConf string , writePIDFile , allowEmpty bool , pidFile , extraConfig string ) * ini . File {
cfg := ini . Empty ( )
2019-04-30 02:08:21 +08:00
2023-02-20 00:12:01 +08:00
if writePIDFile && len ( pidFile ) > 0 {
createPIDFile ( pidFile )
2019-04-30 02:08:21 +08:00
}
2015-02-05 18:12:37 +08:00
2023-02-20 00:12:01 +08:00
isFile , err := util . IsFile ( customConf )
2020-11-28 10:42:08 +08:00
if err != nil {
2023-02-20 00:12:01 +08:00
log . Error ( "Unable to check if %s is a file. Error: %v" , customConf , err )
2020-11-28 10:42:08 +08:00
}
if isFile {
2023-02-20 00:12:01 +08:00
if err := cfg . Append ( customConf ) ; err != nil {
log . Fatal ( "Failed to load custom conf '%s': %v" , customConf , err )
2014-05-26 08:11:25 +08:00
}
2021-12-01 15:50:01 +08:00
} else if ! allowEmpty {
log . Fatal ( "Unable to find configuration file: %q.\nEnsure you are running in the correct environment or set the correct configuration file with -c." , CustomConf )
} // else: no config file, a config file might be created at CustomConf later (might not)
2021-12-08 15:34:23 +08:00
if extraConfig != "" {
2023-02-20 00:12:01 +08:00
if err = cfg . Append ( [ ] byte ( extraConfig ) ) ; err != nil {
2021-12-08 15:34:23 +08:00
log . Fatal ( "Unable to append more config: %v" , err )
}
}
2023-02-20 00:12:01 +08:00
cfg . NameMapper = ini . SnackCase
return cfg
}
2014-07-24 03:15:47 +08:00
2023-02-20 00:12:01 +08:00
// LoadCommonSettings loads common configurations from a configuration provider.
func LoadCommonSettings ( ) {
loadCommonSettingsFrom ( CfgProvider )
}
2019-08-15 22:46:21 +08:00
2023-02-20 00:12:01 +08:00
// loadCommonSettingsFrom loads common configurations from a configuration provider.
func loadCommonSettingsFrom ( cfg ConfigProvider ) {
// WARNNING: don't change the sequence except you know what you are doing.
loadRunModeFrom ( cfg )
loadLogFrom ( cfg )
loadServerFrom ( cfg )
loadSSHFrom ( cfg )
loadOAuth2From ( cfg )
loadSecurityFrom ( cfg )
loadAttachmentFrom ( cfg )
loadLFSFrom ( cfg )
loadTimeFrom ( cfg )
loadRepositoryFrom ( cfg )
loadPictureFrom ( cfg )
loadPackagesFrom ( cfg )
loadActionsFrom ( cfg )
loadUIFrom ( cfg )
loadAdminFrom ( cfg )
loadAPIFrom ( cfg )
loadMetricsFrom ( cfg )
loadCamoFrom ( cfg )
loadI18nFrom ( cfg )
loadGitFrom ( cfg )
loadMirrorFrom ( cfg )
loadMarkupFrom ( cfg )
loadOtherFrom ( cfg )
}
2014-07-25 04:31:59 +08:00
2023-02-20 00:12:01 +08:00
func loadRunModeFrom ( rootCfg ConfigProvider ) {
rootSec := rootCfg . Section ( "" )
RunUser = rootSec . Key ( "RUN_USER" ) . MustString ( user . CurrentUsername ( ) )
2021-10-07 16:52:08 +08:00
// The following is a purposefully undocumented option. Please do not run Gitea as root. It will only cause future headaches.
// Please don't use root as a bandaid to "fix" something that is broken, instead the broken thing should instead be fixed properly.
2023-02-20 00:12:01 +08:00
unsafeAllowRunAsRoot := rootSec . Key ( "I_AM_BEING_UNSAFE_RUNNING_AS_ROOT" ) . MustBool ( false )
2022-12-27 14:00:34 +08:00
RunMode = os . Getenv ( "GITEA_RUN_MODE" )
if RunMode == "" {
2023-02-20 00:12:01 +08:00
RunMode = rootSec . Key ( "RUN_MODE" ) . MustString ( "prod" )
2022-12-27 14:00:34 +08:00
}
2021-10-20 22:37:19 +08:00
IsProd = strings . EqualFold ( RunMode , "prod" )
2014-05-26 08:11:25 +08:00
// Does not check run user when the install lock is off.
2023-02-20 00:12:01 +08:00
installLock := rootCfg . Section ( "security" ) . Key ( "INSTALL_LOCK" ) . MustBool ( false )
if installLock {
2016-08-10 08:41:18 +08:00
currentUser , match := IsRunUserMatchCurrentUser ( RunUser )
if ! match {
2019-04-02 15:48:31 +08:00
log . Fatal ( "Expect user '%s' but current user is: %s" , RunUser , currentUser )
2016-08-10 08:41:18 +08:00
}
2014-05-26 08:11:25 +08:00
}
2021-10-07 16:52:08 +08:00
// check if we run as root
if os . Getuid ( ) == 0 {
if ! unsafeAllowRunAsRoot {
// Special thanks to VLC which inspired the wording of this messaging.
log . Fatal ( "Gitea is not supposed to be run as root. Sorry. If you need to use privileged TCP ports please instead use setcap and the `cap_net_bind_service` permission" )
}
log . Critical ( "You are running Gitea using the root user, and have purposely chosen to skip built-in protections around this. You have been warned against this." )
}
2020-12-22 19:13:50 +08:00
}
2021-05-30 02:44:14 +08:00
// CreateOrAppendToCustomConf creates or updates the custom config.
// Use the callback to set individual values.
2022-10-02 01:26:33 +08:00
func CreateOrAppendToCustomConf ( purpose string , callback func ( cfg * ini . File ) ) {
if CustomConf == "" {
log . Error ( "Custom config path must not be empty" )
return
}
2021-05-30 02:44:14 +08:00
cfg := ini . Empty ( )
isFile , err := util . IsFile ( CustomConf )
if err != nil {
log . Error ( "Unable to check if %s is a file. Error: %v" , CustomConf , err )
}
if isFile {
if err := cfg . Append ( CustomConf ) ; err != nil {
log . Error ( "failed to load custom conf %s: %v" , CustomConf , err )
return
}
}
callback ( cfg )
if err := os . MkdirAll ( filepath . Dir ( CustomConf ) , os . ModePerm ) ; err != nil {
log . Fatal ( "failed to create '%s': %v" , CustomConf , err )
return
}
if err := cfg . SaveTo ( CustomConf ) ; err != nil {
log . Fatal ( "error saving to custom config: %v" , err )
}
2022-10-02 01:26:33 +08:00
log . Info ( "Settings for %s saved to: %q" , purpose , CustomConf )
2021-06-27 18:07:36 +08:00
// Change permissions to be more restrictive
fi , err := os . Stat ( CustomConf )
if err != nil {
log . Error ( "Failed to determine current conf file permissions: %v" , err )
return
}
if fi . Mode ( ) . Perm ( ) > 0 o600 {
if err = os . Chmod ( CustomConf , 0 o600 ) ; err != nil {
log . Warn ( "Failed changing conf file permissions to -rw-------. Consider changing them manually." )
}
}
2021-05-30 02:44:14 +08:00
}
2023-02-20 00:12:01 +08:00
// LoadSettings initializes the settings for normal start up
func LoadSettings ( ) {
LoadDBSetting ( )
loadServiceFrom ( CfgProvider )
loadOAuth2ClientFrom ( CfgProvider )
InitLogs ( false )
loadCacheFrom ( CfgProvider )
loadSessionFrom ( CfgProvider )
loadCorsFrom ( CfgProvider )
loadMailsFrom ( CfgProvider )
loadProxyFrom ( CfgProvider )
loadWebhookFrom ( CfgProvider )
loadMigrationsFrom ( CfgProvider )
loadIndexerFrom ( CfgProvider )
loadTaskFrom ( CfgProvider )
LoadQueueSettings ( )
loadProjectFrom ( CfgProvider )
loadMimeTypeMapFrom ( CfgProvider )
loadFederationFrom ( CfgProvider )
2014-04-11 02:20:58 +08:00
}
2021-06-17 07:32:57 +08:00
2023-02-20 00:12:01 +08:00
// LoadSettingsForInstall initializes the settings for install
func LoadSettingsForInstall ( ) {
LoadDBSetting ( )
loadServiceFrom ( CfgProvider )
loadMailerFrom ( CfgProvider )
2021-06-17 07:32:57 +08:00
}