2014-04-10 12:20:58 -06:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2017-05-29 01:17:15 -06:00
// Copyright 2017 The Gitea Authors. All rights reserved.
2022-11-27 11:20:29 -07:00
// SPDX-License-Identifier: MIT
2014-04-10 12:20:58 -06:00
2014-05-25 18:11:25 -06:00
package setting
2014-04-10 12:20:58 -06:00
import (
2023-02-03 10:22:11 -07:00
"errors"
2019-04-02 01:48:31 -06:00
"fmt"
2014-04-10 12:20:58 -06:00
"os"
"os/exec"
"path"
"path/filepath"
2019-04-28 13:48:46 -06:00
"runtime"
2014-04-10 12:20:58 -06:00
"strings"
2014-07-24 14:31:59 -06:00
"time"
2014-04-10 12:20:58 -06:00
2016-12-22 11:12:23 -07:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/user"
2014-04-10 12:20:58 -06:00
)
2016-11-27 03:14:25 -07:00
// settings
2014-04-10 12:20:58 -06:00
var (
2021-02-19 14:36:43 -07:00
// AppVer is the version of the current build of Gitea. It is set in main.go from main.Version.
AppVer string
2023-04-19 07:40:42 -06:00
// AppBuiltWith represents a human-readable version go runtime build version and build tags. (See main.go formatBuiltWith().)
2021-02-19 14:36:43 -07:00
AppBuiltWith string
// AppStartTime store time gitea has started
AppStartTime time . Time
2023-02-19 09:12:01 -07:00
2021-02-19 14:36:43 -07: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 08:19:19 -06:00
2023-04-19 07:40:42 -06:00
// Other global setting objects
2023-03-16 01:22:54 -06:00
CfgProvider ConfigProvider
CustomPath string // Custom directory path
CustomConf string
RunMode string
RunUser string
IsProd bool
IsWindows bool
2023-04-19 07:40:42 -06:00
// IsInTesting indicates whether the testing is running. A lot of unreliable code causes a lot of nonsense error logs during testing
// TODO: this is only a temporary solution, we should make the test code more reliable
IsInTesting = false
2014-04-10 12:20:58 -06:00
)
2017-11-03 02:56:20 -06: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 06:27:44 -06:00
}
2017-11-03 02:56:20 -06:00
2022-12-13 23:15:11 -07:00
if err != nil {
2023-02-03 10:22:11 -07:00
if ! errors . Is ( err , exec . ErrDot ) {
2022-12-13 23:15:11 -07:00
return "" , err
}
appPath , err = filepath . Abs ( os . Args [ 0 ] )
}
2014-05-25 18:11:25 -06:00
if err != nil {
return "" , err
}
2017-11-03 02:56:20 -06: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-11 14:27:20 -06:00
return strings . ReplaceAll ( appPath , "\\" , "/" ) , err
2017-11-03 02:56:20 -06:00
}
func getWorkPath ( appPath string ) string {
2019-04-29 12:08:21 -06:00
workPath := AppWorkPath
2017-11-03 02:56:20 -06:00
2019-04-29 12:08:21 -06:00
if giteaWorkPath , ok := os . LookupEnv ( "GITEA_WORK_DIR" ) ; ok {
2017-11-03 02:56:20 -06:00
workPath = giteaWorkPath
2019-04-29 12:08:21 -06:00
}
if len ( workPath ) == 0 {
2017-11-03 02:56:20 -06:00
i := strings . LastIndex ( appPath , "/" )
if i == - 1 {
workPath = appPath
} else {
workPath = appPath [ : i ]
}
}
2022-06-06 08:43:17 -06: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-11 14:27:20 -06:00
return strings . ReplaceAll ( workPath , "\\" , "/" )
2015-11-08 14:59:56 -07:00
}
func init ( ) {
2019-04-28 13:48:46 -06:00
IsWindows = runtime . GOOS == "windows"
2023-04-19 07:40:42 -06:00
if AppVer == "" {
AppVer = "dev"
}
2019-04-02 01:48:31 -06:00
// We can rely on log.CanColorStdout being set properly because modules/log/console_windows.go comes before modules/setting/setting.go lexicographically
2023-04-19 07:40:42 -06:00
// By default set this logger at Info - we'll change it later, but we need to start with something.
2021-06-26 18:56:58 -06:00
log . NewLogger ( 0 , "console" , "console" , fmt . Sprintf ( ` { "level": "info", "colorize": %t, "stacktraceLevel": "none"} ` , log . CanColorStdout ) )
2015-11-08 14:59:56 -07:00
var err error
2017-11-03 02:56:20 -06:00
if AppPath , err = getAppPath ( ) ; err != nil {
2019-04-02 01:48:31 -06:00
log . Fatal ( "Failed to get app path: %v" , err )
2014-05-25 18:11:25 -06:00
}
2017-11-03 02:56:20 -06:00
AppWorkPath = getWorkPath ( AppPath )
2014-05-25 18:11:25 -06:00
}
2015-03-18 02:25:55 -06:00
func forcePathSeparator ( path string ) {
if strings . Contains ( path , "\\" ) {
2019-04-02 01:48:31 -06:00
log . Fatal ( "Do not use '\\' or '\\\\' in paths, instead, please use '/' in all places" )
2015-03-18 02:25:55 -06:00
}
}
2016-08-09 18:41:18 -06: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-15 20:49:07 -06:00
if IsWindows || SSH . StartBuiltinServer {
2016-08-09 18:41:18 -06:00
return "" , true
}
currentUser := user . CurrentUsername ( )
return currentUser , runUser == currentUser
}
2019-04-29 12:08:21 -06: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 09:20:35 -06:00
func SetCustomPathAndConf ( providedCustom , providedConf , providedWorkPath string ) {
if len ( providedWorkPath ) != 0 {
AppWorkPath = filepath . ToSlash ( providedWorkPath )
}
2019-04-29 12:08:21 -06:00
if giteaCustom , ok := os . LookupEnv ( "GITEA_CUSTOM" ) ; ok {
CustomPath = giteaCustom
}
if len ( providedCustom ) != 0 {
CustomPath = providedCustom
}
2014-05-25 18:11:25 -06:00
if len ( CustomPath ) == 0 {
2017-11-03 02:56:20 -06:00
CustomPath = path . Join ( AppWorkPath , "custom" )
} else if ! filepath . IsAbs ( CustomPath ) {
CustomPath = path . Join ( AppWorkPath , CustomPath )
2014-05-25 18:11:25 -06:00
}
2019-04-29 12:08:21 -06:00
if len ( providedConf ) != 0 {
CustomConf = providedConf
2017-01-09 04:54:57 -07:00
}
2015-02-05 03:12:37 -07:00
if len ( CustomConf ) == 0 {
2017-11-03 02:56:20 -06:00
CustomConf = path . Join ( CustomPath , "conf/app.ini" )
2017-06-30 21:10:04 -06:00
} else if ! filepath . IsAbs ( CustomConf ) {
2017-11-03 02:56:20 -06:00
CustomConf = path . Join ( CustomPath , CustomConf )
2020-02-02 09:20:20 -07:00
log . Warn ( "Using 'custom' directory as relative origin for configuration file: '%s'" , CustomConf )
2015-02-05 03:12:37 -07:00
}
2019-04-29 12:08:21 -06:00
}
2023-02-19 09:12:01 -07: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 00:50:01 -07:00
2023-02-19 09:12:01 -07:00
if err != nil {
return fmt . Errorf ( "unable to use APP_DATA_PATH %q. Error: %w" , AppDataPath , err )
}
2021-12-01 00:50:01 -07:00
2023-02-19 09:12:01 -07: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 00:50:01 -07:00
}
2023-02-19 09:12:01 -07:00
return nil
2021-12-01 00:50:01 -07:00
}
2023-05-03 21:55:35 -06:00
func Init ( opts * Options ) {
if opts . CustomConf == "" {
opts . CustomConf = CustomConf
2023-04-25 09:06:39 -06:00
}
var err error
2023-05-03 21:55:35 -06:00
CfgProvider , err = newConfigProviderFromFile ( opts )
2023-04-25 09:06:39 -06:00
if err != nil {
2023-05-03 21:55:35 -06:00
log . Fatal ( "Init[%v]: %v" , opts , err )
2023-04-25 09:06:39 -06:00
}
2023-05-03 21:55:35 -06:00
if ! opts . DisableLoadCommonSettings {
loadCommonSettingsFrom ( CfgProvider )
2022-10-16 17:29:26 -06:00
}
2023-02-19 09:12:01 -07:00
}
2019-08-15 08:46:21 -06:00
2023-02-19 09:12:01 -07: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 )
2023-04-30 12:14:57 -06:00
mustCurrentRunUserMatch ( cfg ) // it depends on the SSH config, only non-builtin SSH server requires this check
2023-02-19 09:12:01 -07:00
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-24 14:31:59 -06:00
2023-02-19 09:12:01 -07:00
func loadRunModeFrom ( rootCfg ConfigProvider ) {
rootSec := rootCfg . Section ( "" )
RunUser = rootSec . Key ( "RUN_USER" ) . MustString ( user . CurrentUsername ( ) )
2021-10-07 02:52:08 -06: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-19 09:12:01 -07:00
unsafeAllowRunAsRoot := rootSec . Key ( "I_AM_BEING_UNSAFE_RUNNING_AS_ROOT" ) . MustBool ( false )
2022-12-26 23:00:34 -07:00
RunMode = os . Getenv ( "GITEA_RUN_MODE" )
if RunMode == "" {
2023-02-19 09:12:01 -07:00
RunMode = rootSec . Key ( "RUN_MODE" ) . MustString ( "prod" )
2022-12-26 23:00:34 -07:00
}
2021-10-20 08:37:19 -06:00
IsProd = strings . EqualFold ( RunMode , "prod" )
2014-05-25 18:11:25 -06:00
2021-10-07 02:52:08 -06: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 04:13:50 -07:00
}
2023-04-30 12:14:57 -06:00
func mustCurrentRunUserMatch ( rootCfg ConfigProvider ) {
// Does not check run user when the "InstallLock" is off.
installLock := rootCfg . Section ( "security" ) . Key ( "INSTALL_LOCK" ) . MustBool ( false )
if installLock {
currentUser , match := IsRunUserMatchCurrentUser ( RunUser )
if ! match {
log . Fatal ( "Expect user '%s' but current user is: %s" , RunUser , currentUser )
}
}
}
2023-02-19 09:12:01 -07:00
// LoadSettings initializes the settings for normal start up
func LoadSettings ( ) {
2023-03-16 01:22:54 -06:00
loadDBSetting ( CfgProvider )
2023-02-19 09:12:01 -07:00
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-10 12:20:58 -06:00
}
2021-06-16 17:32:57 -06:00
2023-02-19 09:12:01 -07:00
// LoadSettingsForInstall initializes the settings for install
func LoadSettingsForInstall ( ) {
2023-03-16 01:22:54 -06:00
loadDBSetting ( CfgProvider )
2023-02-19 09:12:01 -07:00
loadServiceFrom ( CfgProvider )
loadMailerFrom ( CfgProvider )
2021-06-16 17:32:57 -06:00
}