/*
** Copyright 2010, Adam Shanks (@ChainsDD)
** Copyright 2008, Zinx Verituse (@zinxv)
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#ifndef SU_h 
#define SU_h 1

#ifdef LOG_TAG
#undef LOG_TAG
#endif
#define LOG_TAG "su"

#ifndef AID_SHELL
#define AID_SHELL (get_shell_uid())
#endif

#ifndef AID_ROOT
#define AID_ROOT  0
#endif

#ifndef AID_SYSTEM
#define AID_SYSTEM (get_system_uid())
#endif

#ifndef AID_RADIO
#define AID_RADIO (get_radio_uid())
#endif

// CyanogenMod-specific behavior
#define CM_ROOT_ACCESS_DISABLED      0
#define CM_ROOT_ACCESS_APPS_ONLY     1
#define CM_ROOT_ACCESS_ADB_ONLY      2
#define CM_ROOT_ACCESS_APPS_AND_ADB  3

// DO NOT CHANGE LINE BELOW, java package name will always be the same
#define JAVA_PACKAGE_NAME "com.topjohnwu.magisk"

// If --rename-manifest-package is used in AAPT, this
// must be changed to correspond to the new APK package name
// See the two Android.mk files for more details.
#ifndef REQUESTOR
#define REQUESTOR JAVA_PACKAGE_NAME
#endif
// This is used if wrapping the fragment classes and activities
// with classes in another package. CM requirement.
#ifndef REQUESTOR_PREFIX
#define REQUESTOR_PREFIX JAVA_PACKAGE_NAME ".superuser"
#endif
#define REQUESTOR_DATA_PATH "/data/data/"
#define REQUESTOR_FILES_PATH REQUESTOR_DATA_PATH REQUESTOR "/files"
#define REQUESTOR_USER_PATH "/data/user/"
#define REQUESTOR_CACHE_PATH "/dev/" REQUESTOR
#define REQUESTOR_DAEMON_PATH "\0MAGISKSU"
// there's no guarantee that the db or files are actually created named as such by
// SQLiteOpenHelper, etc. Though that is the behavior as of current.
// it is up to the Android application to symlink as appropriate.
#define REQUESTOR_DATABASE_PATH REQUESTOR "/databases/su.db"
#define REQUESTOR_MULTIUSER_MODE REQUESTOR_FILES_PATH "/multiuser_mode"

#define DEFAULT_SHELL "/system/bin/sh"

#define xstr(a) str(a)
#define str(a) #a

#ifndef VERSION_CODE
#define VERSION_CODE 1
#endif
#define VERSION "MAGISKSU:" xstr(VERSION_CODE)

#define PROTO_VERSION 1

struct su_initiator {
    pid_t pid;
    unsigned uid;
    unsigned user;
    char bin[PATH_MAX];
    char args[4096];
};

struct su_request {
    unsigned uid;
    int login;
    int keepenv;
    char *shell;
    char *context;
    char *command;
    char **argv;
    int argc;
    int optind;
};

struct su_user_info {
    // the user in android userspace (multiuser)
    // that invoked this action.
    unsigned android_user_id;
    // how su behaves with multiuser. see enum below.
    int multiuser_mode;
    // path to superuser directory. this is populated according
    // to the multiuser mode.
    // this is used to check uid/gid for protecting socket.
    // this is used instead of database, as it is more likely
    // to exist. db will not exist if su has never launched.
    char base_path[PATH_MAX];
    // path to su database. this is populated according
    // to the multiuser mode.
    char database_path[PATH_MAX];
};

struct su_context {
    struct su_initiator from;
    struct su_request to;
    struct su_user_info user;
    mode_t umask;
    char sock_path[PATH_MAX];
};

// multiuser su behavior
typedef enum {
    // only owner can su
    MULTIUSER_MODE_OWNER_ONLY = 0,
    // owner gets a su prompt
    MULTIUSER_MODE_OWNER_MANAGED = 1,
    // user gets a su prompt
    MULTIUSER_MODE_USER = 2,
    MULTIUSER_MODE_NONE = 3,
} multiuser_mode_t;

#define MULTIUSER_VALUE_OWNER_ONLY    "owner"
#define MULTIUSER_VALUE_OWNER_MANAGED "managed"
#define MULTIUSER_VALUE_USER          "user"
#define MULTIUSER_VALUE_NONE          "none"

typedef enum {
    INTERACTIVE = 0,
    DENY = 1,
    ALLOW = 2,
} policy_t;

extern policy_t database_check(struct su_context *ctx);
extern void set_identity(unsigned int uid);
extern int send_request(struct su_context *ctx);
extern int send_result(struct su_context *ctx, policy_t policy);

static inline char *get_command(const struct su_request *to)
{
    if (to->command)
        return to->command;
    if (to->shell)
        return to->shell;
    char* ret = to->argv[to->optind];
    if (ret)
        return ret;
    return DEFAULT_SHELL;
}

void exec_loge(const char* fmt, ...);
void exec_logw(const char* fmt, ...);
void exec_logd(const char* fmt, ...);

int run_daemon();
int connect_daemon(int argc, char *argv[], int ppid);
int su_main(int argc, char *argv[]);
int su_main_nodaemon(int argc, char *argv[]);
// for when you give zero fucks about the state of the child process.
// this version of fork understands you don't care about the child.
// deadbeat dad fork.
int fork_zero_fucks();

void hacks_init();
void hacks_update_context(struct su_context* ctxt);

// fallback to using /system/bin/log.
// can't use liblog.so because this is a static binary.
#ifndef LOGE
#define LOGE exec_loge
#endif
#ifndef LOGD
#define LOGD exec_logd
#endif
#ifndef LOGW
#define LOGW exec_logw
#endif

#if 0
#undef LOGE
#define LOGE(fmt,args...) fprintf(stderr, fmt, ##args)
#undef LOGD
#define LOGD(fmt,args...) fprintf(stderr, fmt, ##args)
#undef LOGW
#define LOGW(fmt,args...) fprintf(stderr, fmt, ##args)
#endif

#include <errno.h>
#include <string.h>
#define PLOGE(fmt,args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno))
#define PLOGEV(fmt,err,args...) LOGE(fmt " failed with %d: %s", ##args, err, strerror(err))

#endif
