<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/node"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/rsync"
	"github.com/lxc/lxd/lxd/state"
	storagePools "github.com/lxc/lxd/lxd/storage"
	storageDrivers "github.com/lxc/lxd/lxd/storage/drivers"
	"github.com/lxc/lxd/shared"
)

func daemonStorageVolumesUnmount(s *state.State) error {
	var storageBackups string
	var storageImages string

	err := s.Node.Transaction(func(tx *db.NodeTx) error {
		nodeConfig, err := node.ConfigLoad(tx)
		if err != nil {
			return err
		}

		storageBackups = nodeConfig.StorageBackupsVolume()
		storageImages = nodeConfig.StorageImagesVolume()

		return nil
	})
	if err != nil {
		return err
	}

	unmount := func(storageType string, source string) error {
		// Parse the source.
		fields := strings.Split(source, "/")
		if len(fields) != 2 {
			return fmt.Errorf("Invalid syntax for volume, must be <pool>/<volume>")
		}

		poolName := fields[0]
		volumeName := fields[1]

		pool, err := storagePools.GetPoolByName(s, poolName)
		if err != nil {
			return err
		}

		// Mount volume.
		_, err = pool.UnmountCustomVolume(project.Default, volumeName, nil)
		if err != nil {
			return errors.Wrapf(err, "Failed to unmount storage volume %q", source)
		}

		return nil
	}

	if storageBackups != "" {
		err := unmount("backups", storageBackups)
		if err != nil {
			return errors.Wrap(err, "Failed to unmount backups storage")
		}
	}

	if storageImages != "" {
		err := unmount("images", storageImages)
		if err != nil {
			return errors.Wrap(err, "Failed to unmount images storage")
		}
	}

	return nil
}

func daemonStorageMount(s *state.State) error {
	var storageBackups string
	var storageImages string
	err := s.Node.Transaction(func(tx *db.NodeTx) error {
		nodeConfig, err := node.ConfigLoad(tx)
		if err != nil {
			return err
		}

		storageBackups = nodeConfig.StorageBackupsVolume()
		storageImages = nodeConfig.StorageImagesVolume()

		return nil
	})
	if err != nil {
		return err
	}

	mount := func(storageType string, source string) error {
		// Parse the source.
		fields := strings.Split(source, "/")
		if len(fields) != 2 {
			return fmt.Errorf("Invalid syntax for volume, must be <pool>/<volume>")
		}

		poolName := fields[0]
		volumeName := fields[1]

		pool, err := storagePools.GetPoolByName(s, poolName)
		if err != nil {
			return err
		}

		// Mount volume.
		err = pool.MountCustomVolume(project.Default, volumeName, nil)
		if err != nil {
			return errors.Wrapf(err, "Failed to mount storage volume %q", source)
		}

		return nil
	}

	if storageBackups != "" {
		err := mount("backups", storageBackups)
		if err != nil {
			return errors.Wrap(err, "Failed to mount backups storage")
		}
	}

	if storageImages != "" {
		err := mount("images", storageImages)
		if err != nil {
			return errors.Wrap(err, "Failed to mount images storage")
		}
	}

	return nil
}

func daemonStorageValidate(s *state.State, target string) error {
	// Check syntax.
	if target == "" {
		return nil
	}

	fields := strings.Split(target, "/")
	if len(fields) != 2 {
		return fmt.Errorf("Invalid syntax for volume, must be <pool>/<volume>")
	}

	poolName := fields[0]
	volumeName := fields[1]

	// Validate pool exists.
	poolID, _, _, err := s.Cluster.GetStoragePool(poolName)
	if err != nil {
		return errors.Wrapf(err, "Unable to load storage pool %q", poolName)
	}

	// Confirm volume exists.
	_, _, err = s.Cluster.GetLocalStoragePoolVolume(project.Default, volumeName, db.StoragePoolVolumeTypeCustom, poolID)
	if err != nil {
		return errors.Wrapf(err, "Unable to load storage volume %q", target)
	}

	snapshots, err := s.Cluster.GetLocalStoragePoolVolumeSnapshotsWithType(project.Default, volumeName, db.StoragePoolVolumeTypeCustom, poolID)
	if err != nil {
		return errors.Wrapf(err, "Unable to load storage volume snapshots %q", target)
	}

	if len(snapshots) != 0 {
		return fmt.Errorf("Storage volumes for use by LXD itself cannot have snapshots")
	}

	pool, err := storagePools.GetPoolByName(s, poolName)
	if err != nil {
		return err
	}

	// Mount volume.
	err = pool.MountCustomVolume(project.Default, volumeName, nil)
	if err != nil {
		return errors.Wrapf(err, "Failed to mount storage volume %q", target)
	}
	defer pool.UnmountCustomVolume(project.Default, volumeName, nil)

	// Validate volume is empty (ignore lost+found).
	volStorageName := project.StorageVolume(project.Default, volumeName)
	mountpoint := storageDrivers.GetVolumeMountPath(poolName, storageDrivers.VolumeTypeCustom, volStorageName)

	entries, err := ioutil.ReadDir(mountpoint)
	if err != nil {
		return errors.Wrapf(err, "Failed to list %q", mountpoint)
	}

	for _, entry := range entries {
		entryName := entry.Name()

		// Don't fail on clean ext4 volumes.
		if entryName == "lost+found" {
			continue
		}

		// Don't fail on systems with snapdir=visible.
		if entryName == ".zfs" {
			continue
		}

		return fmt.Errorf("Storage volume %q isn't empty", target)
	}

	return nil
}

func daemonStorageMove(s *state.State, storageType string, target string) error {
	destPath := shared.VarPath(storageType)

	// Track down the current storage.
	var sourcePool string
	var sourceVolume string

	sourcePath, err := os.Readlink(destPath)
	if err != nil {
		sourcePath = destPath
	} else {
		fields := strings.Split(sourcePath, "/")
		sourcePool = fields[len(fields)-3]
		sourceVolume = fields[len(fields)-1]
	}

	moveContent := func(source string, target string) error {
		// Copy the content.
		_, err := rsync.LocalCopy(source, target, "", false)
		if err != nil {
			return err
		}

		// Remove the source content.
		entries, err := ioutil.ReadDir(source)
		if err != nil {
			return err
		}

		for _, entry := range entries {
			err := os.RemoveAll(filepath.Join(source, entry.Name()))
			if err != nil {
				return err
			}
		}

		return nil
	}

	// Deal with unsetting.
	if target == "" {
		// Things already look correct.
		if sourcePath == destPath {
			return nil
		}

		// Remove the symlink.
		err = os.Remove(destPath)
		if err != nil {
			return errors.Wrapf(err, "Failed to delete storage symlink at %q", destPath)
		}

		// Re-create as a directory.
		err = os.MkdirAll(destPath, 0700)
		if err != nil {
			return errors.Wrapf(err, "Failed to create directory %q", destPath)
		}

		// Move the data across.
		err = moveContent(sourcePath, destPath)
		if err != nil {
			return errors.Wrapf(err, "Failed to move data over to directory %q", destPath)
		}

		pool, err := storagePools.GetPoolByName(s, sourcePool)
		if err != nil {
			return err
		}

		// Unmount old volume.
		projectName, sourceVolumeName := project.StorageVolumeParts(sourceVolume)
		_, err = pool.UnmountCustomVolume(projectName, sourceVolumeName, nil)
		if err != nil {
			return errors.Wrapf(err, `Failed to umount storage volume "%s/%s"`, sourcePool, sourceVolumeName)
		}

		return nil
	}

	// Parse the target.
	fields := strings.Split(target, "/")
	if len(fields) != 2 {
		return fmt.Errorf("Invalid syntax for volume, must be <pool>/<volume>")
	}

	poolName := fields[0]
	volumeName := fields[1]

	pool, err := storagePools.GetPoolByName(s, poolName)
	if err != nil {
		return err
	}

	// Mount volume.
	err = pool.MountCustomVolume(project.Default, volumeName, nil)
	if err != nil {
		return errors.Wrapf(err, "Failed to mount storage volume %q", target)
	}

	// Set ownership & mode.
	volStorageName := project.StorageVolume(project.Default, volumeName)
	mountpoint := storageDrivers.GetVolumeMountPath(poolName, storageDrivers.VolumeTypeCustom, volStorageName)
	destPath = mountpoint

	err = os.Chmod(mountpoint, 0700)
	if err != nil {
		return errors.Wrapf(err, "Failed to set permissions on %q", mountpoint)
	}

	err = os.Chown(mountpoint, 0, 0)
	if err != nil {
		return errors.Wrapf(err, "Failed to set ownership on %q", mountpoint)
	}

	// Handle changes.
	if sourcePath != shared.VarPath(storageType) {
		// Remove the symlink.
		err := os.Remove(shared.VarPath(storageType))
		if err != nil {
			return errors.Wrapf(err, "Failed to remove the new symlink at %q", shared.VarPath(storageType))
		}

		// Create the new symlink.
		err = os.Symlink(destPath, shared.VarPath(storageType))
		if err != nil {
			return errors.Wrapf(err, "Failed to create the new symlink at %q", shared.VarPath(storageType))
		}

		// Move the data across.
		err = moveContent(sourcePath, destPath)
		if err != nil {
			return errors.Wrapf(err, "Failed to move data over to directory %q", destPath)
		}

		pool, err := storagePools.GetPoolByName(s, sourcePool)
		if err != nil {
			return err
		}

		// Unmount old volume.
		projectName, sourceVolumeName := project.StorageVolumeParts(sourceVolume)
		_, err = pool.UnmountCustomVolume(projectName, sourceVolumeName, nil)
		if err != nil {
			return errors.Wrapf(err, `Failed to umount storage volume "%s/%s"`, sourcePool, sourceVolumeName)
		}

		return nil
	}

	sourcePath = shared.VarPath(storageType) + ".temp"

	// Rename the existing storage.
	err = os.Rename(shared.VarPath(storageType), sourcePath)
	if err != nil {
		return errors.Wrapf(err, "Failed to rename existing storage %q", shared.VarPath(storageType))
	}

	// Create the new symlink.
	err = os.Symlink(destPath, shared.VarPath(storageType))
	if err != nil {
		return errors.Wrapf(err, "Failed to create the new symlink at %q", shared.VarPath(storageType))
	}

	// Move the data across.
	err = moveContent(sourcePath, destPath)
	if err != nil {
		return errors.Wrapf(err, "Failed to move data over to directory %q", destPath)
	}

	// Remove the old data.
	err = os.RemoveAll(sourcePath)
	if err != nil {
		return errors.Wrapf(err, "Failed to cleanup old directory %q", sourcePath)
	}

	return nil
}
<commit_msg>lxd/daemon: Introduce daemonStorageSplitVolume<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/node"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/rsync"
	"github.com/lxc/lxd/lxd/state"
	storagePools "github.com/lxc/lxd/lxd/storage"
	storageDrivers "github.com/lxc/lxd/lxd/storage/drivers"
	"github.com/lxc/lxd/shared"
)

func daemonStorageVolumesUnmount(s *state.State) error {
	var storageBackups string
	var storageImages string

	err := s.Node.Transaction(func(tx *db.NodeTx) error {
		nodeConfig, err := node.ConfigLoad(tx)
		if err != nil {
			return err
		}

		storageBackups = nodeConfig.StorageBackupsVolume()
		storageImages = nodeConfig.StorageImagesVolume()

		return nil
	})
	if err != nil {
		return err
	}

	unmount := func(storageType string, source string) error {
		// Parse the source.
		poolName, volumeName, err := daemonStorageSplitVolume(source)
		if err != nil {
			return err
		}

		pool, err := storagePools.GetPoolByName(s, poolName)
		if err != nil {
			return err
		}

		// Mount volume.
		_, err = pool.UnmountCustomVolume(project.Default, volumeName, nil)
		if err != nil {
			return errors.Wrapf(err, "Failed to unmount storage volume %q", source)
		}

		return nil
	}

	if storageBackups != "" {
		err := unmount("backups", storageBackups)
		if err != nil {
			return errors.Wrap(err, "Failed to unmount backups storage")
		}
	}

	if storageImages != "" {
		err := unmount("images", storageImages)
		if err != nil {
			return errors.Wrap(err, "Failed to unmount images storage")
		}
	}

	return nil
}

func daemonStorageMount(s *state.State) error {
	var storageBackups string
	var storageImages string
	err := s.Node.Transaction(func(tx *db.NodeTx) error {
		nodeConfig, err := node.ConfigLoad(tx)
		if err != nil {
			return err
		}

		storageBackups = nodeConfig.StorageBackupsVolume()
		storageImages = nodeConfig.StorageImagesVolume()

		return nil
	})
	if err != nil {
		return err
	}

	mount := func(storageType string, source string) error {
		// Parse the source.
		poolName, volumeName, err := daemonStorageSplitVolume(source)
		if err != nil {
			return err
		}

		pool, err := storagePools.GetPoolByName(s, poolName)
		if err != nil {
			return err
		}

		// Mount volume.
		err = pool.MountCustomVolume(project.Default, volumeName, nil)
		if err != nil {
			return errors.Wrapf(err, "Failed to mount storage volume %q", source)
		}

		return nil
	}

	if storageBackups != "" {
		err := mount("backups", storageBackups)
		if err != nil {
			return errors.Wrap(err, "Failed to mount backups storage")
		}
	}

	if storageImages != "" {
		err := mount("images", storageImages)
		if err != nil {
			return errors.Wrap(err, "Failed to mount images storage")
		}
	}

	return nil
}

func daemonStorageSplitVolume(volume string) (string, string, error) {
	fields := strings.Split(volume, "/")
	if len(fields) != 2 {
		return "", "", fmt.Errorf("Invalid syntax for volume, must be <pool>/<volume>")
	}

	poolName := fields[0]
	volumeName := fields[1]

	return poolName, volumeName, nil
}

func daemonStorageValidate(s *state.State, target string) error {
	// Check syntax.
	if target == "" {
		return nil
	}

	poolName, volumeName, err := daemonStorageSplitVolume(target)
	if err != nil {
		return err
	}

	// Validate pool exists.
	poolID, _, _, err := s.Cluster.GetStoragePool(poolName)
	if err != nil {
		return errors.Wrapf(err, "Unable to load storage pool %q", poolName)
	}

	// Confirm volume exists.
	_, _, err = s.Cluster.GetLocalStoragePoolVolume(project.Default, volumeName, db.StoragePoolVolumeTypeCustom, poolID)
	if err != nil {
		return errors.Wrapf(err, "Unable to load storage volume %q", target)
	}

	snapshots, err := s.Cluster.GetLocalStoragePoolVolumeSnapshotsWithType(project.Default, volumeName, db.StoragePoolVolumeTypeCustom, poolID)
	if err != nil {
		return errors.Wrapf(err, "Unable to load storage volume snapshots %q", target)
	}

	if len(snapshots) != 0 {
		return fmt.Errorf("Storage volumes for use by LXD itself cannot have snapshots")
	}

	pool, err := storagePools.GetPoolByName(s, poolName)
	if err != nil {
		return err
	}

	// Mount volume.
	err = pool.MountCustomVolume(project.Default, volumeName, nil)
	if err != nil {
		return errors.Wrapf(err, "Failed to mount storage volume %q", target)
	}
	defer pool.UnmountCustomVolume(project.Default, volumeName, nil)

	// Validate volume is empty (ignore lost+found).
	volStorageName := project.StorageVolume(project.Default, volumeName)
	mountpoint := storageDrivers.GetVolumeMountPath(poolName, storageDrivers.VolumeTypeCustom, volStorageName)

	entries, err := ioutil.ReadDir(mountpoint)
	if err != nil {
		return errors.Wrapf(err, "Failed to list %q", mountpoint)
	}

	for _, entry := range entries {
		entryName := entry.Name()

		// Don't fail on clean ext4 volumes.
		if entryName == "lost+found" {
			continue
		}

		// Don't fail on systems with snapdir=visible.
		if entryName == ".zfs" {
			continue
		}

		return fmt.Errorf("Storage volume %q isn't empty", target)
	}

	return nil
}

func daemonStorageMove(s *state.State, storageType string, target string) error {
	destPath := shared.VarPath(storageType)

	// Track down the current storage.
	var sourcePool string
	var sourceVolume string

	sourcePath, err := os.Readlink(destPath)
	if err != nil {
		sourcePath = destPath
	} else {
		fields := strings.Split(sourcePath, "/")
		sourcePool = fields[len(fields)-3]
		sourceVolume = fields[len(fields)-1]
	}

	moveContent := func(source string, target string) error {
		// Copy the content.
		_, err := rsync.LocalCopy(source, target, "", false)
		if err != nil {
			return err
		}

		// Remove the source content.
		entries, err := ioutil.ReadDir(source)
		if err != nil {
			return err
		}

		for _, entry := range entries {
			err := os.RemoveAll(filepath.Join(source, entry.Name()))
			if err != nil {
				return err
			}
		}

		return nil
	}

	// Deal with unsetting.
	if target == "" {
		// Things already look correct.
		if sourcePath == destPath {
			return nil
		}

		// Remove the symlink.
		err = os.Remove(destPath)
		if err != nil {
			return errors.Wrapf(err, "Failed to delete storage symlink at %q", destPath)
		}

		// Re-create as a directory.
		err = os.MkdirAll(destPath, 0700)
		if err != nil {
			return errors.Wrapf(err, "Failed to create directory %q", destPath)
		}

		// Move the data across.
		err = moveContent(sourcePath, destPath)
		if err != nil {
			return errors.Wrapf(err, "Failed to move data over to directory %q", destPath)
		}

		pool, err := storagePools.GetPoolByName(s, sourcePool)
		if err != nil {
			return err
		}

		// Unmount old volume.
		projectName, sourceVolumeName := project.StorageVolumeParts(sourceVolume)
		_, err = pool.UnmountCustomVolume(projectName, sourceVolumeName, nil)
		if err != nil {
			return errors.Wrapf(err, `Failed to umount storage volume "%s/%s"`, sourcePool, sourceVolumeName)
		}

		return nil
	}

	// Parse the target.
	poolName, volumeName, err := daemonStorageSplitVolume(target)
	if err != nil {
		return err
	}

	pool, err := storagePools.GetPoolByName(s, poolName)
	if err != nil {
		return err
	}

	// Mount volume.
	err = pool.MountCustomVolume(project.Default, volumeName, nil)
	if err != nil {
		return errors.Wrapf(err, "Failed to mount storage volume %q", target)
	}

	// Set ownership & mode.
	volStorageName := project.StorageVolume(project.Default, volumeName)
	mountpoint := storageDrivers.GetVolumeMountPath(poolName, storageDrivers.VolumeTypeCustom, volStorageName)
	destPath = mountpoint

	err = os.Chmod(mountpoint, 0700)
	if err != nil {
		return errors.Wrapf(err, "Failed to set permissions on %q", mountpoint)
	}

	err = os.Chown(mountpoint, 0, 0)
	if err != nil {
		return errors.Wrapf(err, "Failed to set ownership on %q", mountpoint)
	}

	// Handle changes.
	if sourcePath != shared.VarPath(storageType) {
		// Remove the symlink.
		err := os.Remove(shared.VarPath(storageType))
		if err != nil {
			return errors.Wrapf(err, "Failed to remove the new symlink at %q", shared.VarPath(storageType))
		}

		// Create the new symlink.
		err = os.Symlink(destPath, shared.VarPath(storageType))
		if err != nil {
			return errors.Wrapf(err, "Failed to create the new symlink at %q", shared.VarPath(storageType))
		}

		// Move the data across.
		err = moveContent(sourcePath, destPath)
		if err != nil {
			return errors.Wrapf(err, "Failed to move data over to directory %q", destPath)
		}

		pool, err := storagePools.GetPoolByName(s, sourcePool)
		if err != nil {
			return err
		}

		// Unmount old volume.
		projectName, sourceVolumeName := project.StorageVolumeParts(sourceVolume)
		_, err = pool.UnmountCustomVolume(projectName, sourceVolumeName, nil)
		if err != nil {
			return errors.Wrapf(err, `Failed to umount storage volume "%s/%s"`, sourcePool, sourceVolumeName)
		}

		return nil
	}

	sourcePath = shared.VarPath(storageType) + ".temp"

	// Rename the existing storage.
	err = os.Rename(shared.VarPath(storageType), sourcePath)
	if err != nil {
		return errors.Wrapf(err, "Failed to rename existing storage %q", shared.VarPath(storageType))
	}

	// Create the new symlink.
	err = os.Symlink(destPath, shared.VarPath(storageType))
	if err != nil {
		return errors.Wrapf(err, "Failed to create the new symlink at %q", shared.VarPath(storageType))
	}

	// Move the data across.
	err = moveContent(sourcePath, destPath)
	if err != nil {
		return errors.Wrapf(err, "Failed to move data over to directory %q", destPath)
	}

	// Remove the old data.
	err = os.RemoveAll(sourcePath)
	if err != nil {
		return errors.Wrapf(err, "Failed to cleanup old directory %q", sourcePath)
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/db/cluster"
	deviceConfig "github.com/lxc/lxd/lxd/device/config"
	"github.com/lxc/lxd/lxd/instance"
	"github.com/lxc/lxd/lxd/instance/instancetype"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
)

func doProfileUpdate(d *Daemon, projectName string, name string, id int64, profile *api.Profile, req api.ProfilePut) error {
	// Check project limits.
	err := d.db.Cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
		return project.AllowProfileUpdate(tx, projectName, name, req)
	})
	if err != nil {
		return err
	}

	// Quick checks.
	err = instance.ValidConfig(d.os, req.Config, false, instancetype.Any)
	if err != nil {
		return err
	}

	// Profiles can be applied to any instance type, so just use instancetype.Any type for validation so that
	// instance type specific validation checks are not performed.
	err = instance.ValidDevices(d.State(), projectName, instancetype.Any, deviceConfig.NewDevices(req.Devices), false)
	if err != nil {
		return err
	}

	insts, err := getProfileInstancesInfo(d.db.Cluster, projectName, name)
	if err != nil {
		return fmt.Errorf("Failed to query instances associated with profile %q: %w", name, err)
	}

	// Check if the root disk device's pool is supposed to be changed or removed and prevent that if there are
	// instances using that root disk device.
	oldProfileRootDiskDeviceKey, oldProfileRootDiskDevice, _ := shared.GetRootDiskDevice(profile.Devices)
	_, newProfileRootDiskDevice, _ := shared.GetRootDiskDevice(req.Devices)
	if len(insts) > 0 && oldProfileRootDiskDevice["pool"] != "" && newProfileRootDiskDevice["pool"] == "" || (oldProfileRootDiskDevice["pool"] != newProfileRootDiskDevice["pool"]) {
		// Check for instances using the device.
		for _, inst := range insts {
			// Check if the device is locally overridden.
			k, v, _ := shared.GetRootDiskDevice(inst.Devices.CloneNative())
			if k != "" && v["pool"] != "" {
				continue
			}

			// Check what profile the device comes from by working backwards along the profiles list.
			for i := len(inst.Profiles) - 1; i >= 0; i-- {
				_, profile, err := d.db.Cluster.GetProfile(projectName, inst.Profiles[i].Name)
				if err != nil {
					return err
				}

				// Check if we find a match for the device.
				_, ok := profile.Devices[oldProfileRootDiskDeviceKey]
				if ok {
					// Found the profile.
					if inst.Profiles[i].Name == name {
						// If it's the current profile, then we can't modify that root device.
						return fmt.Errorf("At least one instance relies on this profile's root disk device")
					}

					// If it's not, then move on to the next instance.
					break
				}
			}
		}
	}

	// Update the database.
	err = d.db.Cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
		devices, err := cluster.APIToDevices(req.Devices)
		if err != nil {
			return err
		}

		err = cluster.UpdateProfile(ctx, tx.Tx(), projectName, name, cluster.Profile{
			Project:     projectName,
			Name:        name,
			Description: req.Description,
		})
		if err != nil {
			return err
		}

		id, err := cluster.GetProfileID(ctx, tx.Tx(), projectName, name)
		if err != nil {
			return err
		}

		err = cluster.UpdateProfileConfig(ctx, tx.Tx(), id, req.Config)
		if err != nil {
			return err
		}

		err = cluster.UpdateProfileDevices(ctx, tx.Tx(), id, devices)
		if err != nil {
			return nil
		}

		newProfiles, err := cluster.GetProfilesIfEnabled(ctx, tx.Tx(), projectName, []string{name})
		if err != nil {
			return err
		}

		if len(newProfiles) != 1 {
			return fmt.Errorf("Failed to find profile %q in project %q", name, projectName)
		}

		apiProfile, err := newProfiles[0].ToAPI(ctx, tx.Tx())
		if err != nil {
			return err
		}

		// Update the profile on our current list of instances.
		for i := range insts {
			for j, profile := range insts[i].Profiles {
				if profile.Name == apiProfile.Name {
					insts[i].Profiles[j] = *apiProfile
					break
				}
			}
		}

		return nil
	})
	if err != nil {
		return err
	}

	// Update all the instances on this node using the profile. Must be done after db.TxCommit due to DB lock.
	serverName := d.State().ServerName

	failures := map[*db.InstanceArgs]error{}
	for _, it := range insts {
		inst := it // Local var for instance pointer.
		err := doProfileUpdateInstance(d, name, profile.ProfilePut, serverName, inst)
		if err != nil {
			failures[&inst] = err
		}
	}

	if len(failures) != 0 {
		msg := "The following instances failed to update (profile change still saved):\n"
		for inst, err := range failures {
			msg += fmt.Sprintf(" - Project: %s, Instance: %s: %v\n", inst.Project, inst.Name, err)
		}

		return fmt.Errorf("%s", msg)
	}

	return nil
}

// Like doProfileUpdate but does not update the database, since it was already
// updated by doProfileUpdate itself, called on the notifying node.
func doProfileUpdateCluster(d *Daemon, projectName string, name string, old api.ProfilePut) error {
	serverName := d.State().ServerName

	insts, err := getProfileInstancesInfo(d.db.Cluster, projectName, name)
	if err != nil {
		return fmt.Errorf("Failed to query instances associated with profile %q: %w", name, err)
	}

	failures := map[*db.InstanceArgs]error{}
	for _, it := range insts {
		inst := it // Local var for instance pointer.
		err := doProfileUpdateInstance(d, name, old, serverName, inst)
		if err != nil {
			failures[&inst] = err
		}
	}

	if len(failures) != 0 {
		msg := "The following instances failed to update (profile change still saved):\n"
		for inst, err := range failures {
			msg += fmt.Sprintf(" - Project: %s, Instance: %s: %v\n", inst.Project, inst.Name, err)
		}

		return fmt.Errorf("%s", msg)
	}

	return nil
}

// Profile update of a single instance.
func doProfileUpdateInstance(d *Daemon, name string, old api.ProfilePut, nodeName string, args db.InstanceArgs) error {
	if args.Node != "" && args.Node != nodeName {
		// No-op, this instance does not belong to this node.
		return nil
	}

	profileNames := make([]string, 0, len(args.Profiles))
	for _, profile := range args.Profiles {
		profileNames = append(profileNames, profile.Name)
	}

	profiles, err := d.db.Cluster.GetProfiles(args.Project, profileNames)
	if err != nil {
		return err
	}

	for i, profile := range args.Profiles {
		if profile.Name == name {
			// Overwrite the new config from the database with the old config and devices.
			profiles[i].Config = old.Config
			profiles[i].Devices = old.Devices
			break
		}
	}

	// Load the instance using the old profile config.
	inst, err := instance.Load(d.State(), args, profiles)
	if err != nil {
		return err
	}

	// Update will internally load the new profile configs and detect the changes to apply.
	return inst.Update(db.InstanceArgs{
		Architecture: inst.Architecture(),
		Config:       inst.LocalConfig(),
		Description:  inst.Description(),
		Devices:      inst.LocalDevices(),
		Ephemeral:    inst.IsEphemeral(),
		Profiles:     inst.Profiles(),
		Project:      inst.Project(),
		Type:         inst.Type(),
		Snapshot:     inst.IsSnapshot(),
	}, true)
}

// Query the db for information about instances associated with the given profile.
func getProfileInstancesInfo(dbCluster *db.Cluster, projectName string, profileName string) (map[int]db.InstanceArgs, error) {
	// Query the db for information about instances associated with the given profile.
	projectInstNames, err := dbCluster.GetInstancesWithProfile(projectName, profileName)
	if err != nil {
		return nil, fmt.Errorf("Failed to query instances with profile %q: %w", profileName, err)
	}

	var instances map[int]db.InstanceArgs
	err = dbCluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
		var dbInstances []cluster.Instance

		for instProject, instNames := range projectInstNames {
			for _, instName := range instNames {
				dbInst, err := cluster.GetInstance(ctx, tx.Tx(), instProject, instName)
				if err != nil {
					return err
				}

				dbInstances = append(dbInstances, *dbInst)
			}
		}

		instances, err = tx.InstancesToInstanceArgs(ctx, true, dbInstances...)
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return nil, fmt.Errorf("Failed to fetch instances: %w", err)
	}

	return instances, nil
}
<commit_msg>lxd/profiles/utils: instance.Load usage in doProfileUpdateInstance<commit_after>package main

import (
	"context"
	"fmt"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/db/cluster"
	deviceConfig "github.com/lxc/lxd/lxd/device/config"
	"github.com/lxc/lxd/lxd/instance"
	"github.com/lxc/lxd/lxd/instance/instancetype"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
)

func doProfileUpdate(d *Daemon, projectName string, profileName string, id int64, profile *api.Profile, req api.ProfilePut) error {
	// Check project limits.
	err := d.db.Cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
		return project.AllowProfileUpdate(tx, projectName, profileName, req)
	})
	if err != nil {
		return err
	}

	// Quick checks.
	err = instance.ValidConfig(d.os, req.Config, false, instancetype.Any)
	if err != nil {
		return err
	}

	// Profiles can be applied to any instance type, so just use instancetype.Any type for validation so that
	// instance type specific validation checks are not performed.
	err = instance.ValidDevices(d.State(), projectName, instancetype.Any, deviceConfig.NewDevices(req.Devices), false)
	if err != nil {
		return err
	}

	insts, err := getProfileInstancesInfo(d.db.Cluster, projectName, profileName)
	if err != nil {
		return fmt.Errorf("Failed to query instances associated with profile %q: %w", profileName, err)
	}

	// Check if the root disk device's pool is supposed to be changed or removed and prevent that if there are
	// instances using that root disk device.
	oldProfileRootDiskDeviceKey, oldProfileRootDiskDevice, _ := shared.GetRootDiskDevice(profile.Devices)
	_, newProfileRootDiskDevice, _ := shared.GetRootDiskDevice(req.Devices)
	if len(insts) > 0 && oldProfileRootDiskDevice["pool"] != "" && newProfileRootDiskDevice["pool"] == "" || (oldProfileRootDiskDevice["pool"] != newProfileRootDiskDevice["pool"]) {
		// Check for instances using the device.
		for _, inst := range insts {
			// Check if the device is locally overridden.
			k, v, _ := shared.GetRootDiskDevice(inst.Devices.CloneNative())
			if k != "" && v["pool"] != "" {
				continue
			}

			// Check what profile the device comes from by working backwards along the profiles list.
			for i := len(inst.Profiles) - 1; i >= 0; i-- {
				_, profile, err := d.db.Cluster.GetProfile(projectName, inst.Profiles[i].Name)
				if err != nil {
					return err
				}

				// Check if we find a match for the device.
				_, ok := profile.Devices[oldProfileRootDiskDeviceKey]
				if ok {
					// Found the profile.
					if inst.Profiles[i].Name == profileName {
						// If it's the current profile, then we can't modify that root device.
						return fmt.Errorf("At least one instance relies on this profile's root disk device")
					}

					// If it's not, then move on to the next instance.
					break
				}
			}
		}
	}

	// Update the database.
	err = d.db.Cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
		devices, err := cluster.APIToDevices(req.Devices)
		if err != nil {
			return err
		}

		err = cluster.UpdateProfile(ctx, tx.Tx(), projectName, profileName, cluster.Profile{
			Project:     projectName,
			Name:        profileName,
			Description: req.Description,
		})
		if err != nil {
			return err
		}

		id, err := cluster.GetProfileID(ctx, tx.Tx(), projectName, profileName)
		if err != nil {
			return err
		}

		err = cluster.UpdateProfileConfig(ctx, tx.Tx(), id, req.Config)
		if err != nil {
			return err
		}

		err = cluster.UpdateProfileDevices(ctx, tx.Tx(), id, devices)
		if err != nil {
			return nil
		}

		newProfiles, err := cluster.GetProfilesIfEnabled(ctx, tx.Tx(), projectName, []string{profileName})
		if err != nil {
			return err
		}

		if len(newProfiles) != 1 {
			return fmt.Errorf("Failed to find profile %q in project %q", profileName, projectName)
		}

		return nil
	})
	if err != nil {
		return err
	}

	// Update all the instances on this node using the profile. Must be done after db.TxCommit due to DB lock.
	serverName := d.State().ServerName

	failures := map[*db.InstanceArgs]error{}
	for _, it := range insts {
		inst := it // Local var for instance pointer.
		err := doProfileUpdateInstance(d, serverName, inst)
		if err != nil {
			failures[&inst] = err
		}
	}

	if len(failures) != 0 {
		msg := "The following instances failed to update (profile change still saved):\n"
		for inst, err := range failures {
			msg += fmt.Sprintf(" - Project: %s, Instance: %s: %v\n", inst.Project, inst.Name, err)
		}

		return fmt.Errorf("%s", msg)
	}

	return nil
}

// Like doProfileUpdate but does not update the database, since it was already
// updated by doProfileUpdate itself, called on the notifying node.
func doProfileUpdateCluster(d *Daemon, projectName string, profileName string, old api.ProfilePut) error {
	serverName := d.State().ServerName

	insts, err := getProfileInstancesInfo(d.db.Cluster, projectName, profileName)
	if err != nil {
		return fmt.Errorf("Failed to query instances associated with profile %q: %w", profileName, err)
	}

	failures := map[*db.InstanceArgs]error{}
	for _, it := range insts {
		inst := it // Local var for instance pointer.

		for i, profile := range inst.Profiles {
			if profile.Name == profileName {
				// As profile has already been updated in the database by this point, overwrite the
				// new config from the database with the old config and devices, so that
				// doProfileUpdateInstance will detect the changes and apply them.
				inst.Profiles[i].Config = old.Config
				inst.Profiles[i].Devices = old.Devices
				break
			}
		}

		err := doProfileUpdateInstance(d, serverName, inst)
		if err != nil {
			failures[&inst] = err
		}
	}

	if len(failures) != 0 {
		msg := "The following instances failed to update (profile change still saved):\n"
		for inst, err := range failures {
			msg += fmt.Sprintf(" - Project: %s, Instance: %s: %v\n", inst.Project, inst.Name, err)
		}

		return fmt.Errorf("%s", msg)
	}

	return nil
}

// Profile update of a single instance.
func doProfileUpdateInstance(d *Daemon, nodeName string, args db.InstanceArgs) error {
	if args.Node != "" && args.Node != nodeName {
		// No-op, this instance does not belong to this node.
		return nil
	}

	profileNames := make([]string, 0, len(args.Profiles))
	for _, profile := range args.Profiles {
		profileNames = append(profileNames, profile.Name)
	}

	profiles, err := d.db.Cluster.GetProfiles(args.Project, profileNames)
	if err != nil {
		return err
	}

	// Load the instance using the old profile config.
	inst, err := instance.Load(d.State(), args)
	if err != nil {
		return err
	}

	// Update will internally load the new profile configs and detect the changes to apply.
	return inst.Update(db.InstanceArgs{
		Architecture: inst.Architecture(),
		Config:       inst.LocalConfig(),
		Description:  inst.Description(),
		Devices:      inst.LocalDevices(),
		Ephemeral:    inst.IsEphemeral(),
		Profiles:     profiles, // Supply with new profile config.
		Project:      inst.Project(),
		Type:         inst.Type(),
		Snapshot:     inst.IsSnapshot(),
	}, true)
}

// Query the db for information about instances associated with the given profile.
func getProfileInstancesInfo(dbCluster *db.Cluster, projectName string, profileName string) (map[int]db.InstanceArgs, error) {
	// Query the db for information about instances associated with the given profile.
	projectInstNames, err := dbCluster.GetInstancesWithProfile(projectName, profileName)
	if err != nil {
		return nil, fmt.Errorf("Failed to query instances with profile %q: %w", profileName, err)
	}

	var instances map[int]db.InstanceArgs
	err = dbCluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
		var dbInstances []cluster.Instance

		for instProject, instNames := range projectInstNames {
			for _, instName := range instNames {
				dbInst, err := cluster.GetInstance(ctx, tx.Tx(), instProject, instName)
				if err != nil {
					return err
				}

				dbInstances = append(dbInstances, *dbInst)
			}
		}

		instances, err = tx.InstancesToInstanceArgs(ctx, true, dbInstances...)
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return nil, fmt.Errorf("Failed to fetch instances: %w", err)
	}

	return instances, nil
}
<|endoftext|>


<commit_before>// Package metric provides functions for measuring the difference between the
// values predicted by a model and the values actually observed.
package metric

import (
	"math"
)

// MSE computes the mean-square error.
//
// https://en.wikipedia.org/wiki/Mean_squared_error
func MSE(predictions, observations []float64) float64 {
	var sum, Δ float64

	for i := range observations {
		Δ = predictions[i] - observations[i]
		sum += Δ * Δ
	}

	return sum / float64(len(observations))
}

// RMSE computes the root-mean-square error.
//
// https://en.wikipedia.org/wiki/Root-mean-square_deviation
func RMSE(predictions, observations []float64) float64 {
	return math.Sqrt(MSE(predictions, observations))
}

// NRMSE computes the normalized root-mean-square error.
//
// https://en.wikipedia.org/wiki/Root-mean-square_deviation#Normalized_root-mean-square_deviation
func NRMSE(predictions, observations []float64) float64 {
	count := len(observations)
	if count == 0 {
		return 0
	}

	min, max := observations[0], observations[0]
	for i := 1; i < count; i++ {
		if observations[i] < min {
			min = observations[i]
		}
		if observations[i] > max {
			max = observations[i]
		}
	}

	return RMSE(predictions, observations) / (max - min)
}
<commit_msg>Changed the argument order in metric<commit_after>// Package metric provides functions for measuring the difference between the
// values predicted by a model and the values actually observed.
package metric

import (
	"math"
)

// MSE computes the mean-square error.
//
// https://en.wikipedia.org/wiki/Mean_squared_error
func MSE(observations, predictions []float64) float64 {
	var sum, Δ float64

	for i := range observations {
		Δ = predictions[i] - observations[i]
		sum += Δ * Δ
	}

	return sum / float64(len(observations))
}

// RMSE computes the root-mean-square error.
//
// https://en.wikipedia.org/wiki/Root-mean-square_deviation
func RMSE(observations, predictions []float64) float64 {
	return math.Sqrt(MSE(observations, predictions))
}

// NRMSE computes the normalized root-mean-square error.
//
// https://en.wikipedia.org/wiki/Root-mean-square_deviation#Normalized_root-mean-square_deviation
func NRMSE(observations, predictions []float64) float64 {
	count := len(observations)
	if count == 0 {
		return 0
	}

	min, max := observations[0], observations[0]
	for i := 1; i < count; i++ {
		if observations[i] < min {
			min = observations[i]
		}
		if observations[i] > max {
			max = observations[i]
		}
	}

	return RMSE(observations, predictions) / (max - min)
}
<|endoftext|>


<commit_before>package miner

import (
	"errors"
	"github.com/lisgie/bazo_miner/protocol"
	"github.com/lisgie/bazo_miner/storage"
	"golang.org/x/crypto/sha3"
)

func isRootKey(hash [32]byte) bool {
	_, exists := storage.RootKeys[hash]
	return exists
}

//for normal accounts, it
func accStateChange(txSlice []*protocol.AccTx) error {

	for _, tx := range txSlice {
		switch tx.Header {
		case 1:
			//first bit set, given account will be a new root account
			newAcc := protocol.Account{Address: tx.PubKey}
			storage.RootKeys[sha3.Sum256(tx.PubKey[:])] = &newAcc
			continue
		case 2:
			//second bit set, delete account from root account
			delete(storage.RootKeys, sha3.Sum256(tx.PubKey[:]))
			continue
		}

		//create a regular account
		addressHash := sha3.Sum256(tx.PubKey[:])
		acc := storage.GetAccountFromHash(addressHash)
		if acc != nil {
			logger.Printf("CRITICAL: Address already exists in the state: %x\n", addressHash[0:4])
			return errors.New("CRITICAL: Address already exists in the state")
		}
		newAcc := protocol.Account{Address: tx.PubKey}
		storage.State[addressHash] = &newAcc
	}
	return nil
}

func fundsStateChange(txSlice []*protocol.FundsTx) error {

	for index, tx := range txSlice {

		var err error
		//check if we have to issue new coins
		for hash, rootAcc := range storage.RootKeys {
			if hash == tx.From {
				logger.Printf("Root Key Transaction: %x\n", hash[0:8])

				if rootAcc.Balance+tx.Amount+tx.Fee > protocol.MAX_MONEY {
					logger.Printf("Root Account overflows (%v) with given transaction amount (%v) and fee (%v).\n", rootAcc.Balance, tx.Amount, tx.Fee)
					err = errors.New("Sender does not exist in the State.")
				}

				rootAcc.Balance += tx.Amount
				rootAcc.Balance += tx.Fee
			}
		}

		accSender, accReceiver := storage.GetAccountFromHash(tx.From), storage.GetAccountFromHash(tx.To)
		if accSender == nil {
			logger.Printf("CRITICAL: Sender does not exist in the State: %x\n", tx.From[0:8])
			err = errors.New("Sender does not exist in the State.")
		}

		if accReceiver == nil {
			logger.Printf("CRITICAL: Receiver does not exist in the State: %x\n", tx.To[0:8])
			err = errors.New("Receiver does not exist in the State.")
		}

		//also check for txCnt
		if tx.TxCnt != accSender.TxCnt {
			logger.Printf("Sender txCnt does not match: %v (tx.txCnt) vs. %v (state txCnt)\n", tx.TxCnt, accSender.TxCnt)
			err = errors.New("TxCnt mismatch!")
		}

		if (tx.Amount + tx.Fee) > accSender.Balance {
			logger.Printf("Sender does not have enough balance: %x\n", accSender.Balance)
			err = errors.New("Sender does not have enough funds for the transaction.")
		}

		//overflow protection
		if tx.Amount+accReceiver.Balance > protocol.MAX_MONEY {
			logger.Printf("Transaction amount (%v) would lead to balance overflow at the receiver account (%v)\n", tx.Amount, accReceiver.Balance)
			err = errors.New("Transaction amount would lead to balance overflow at the receiver account\n")
		}

		if err != nil {
			//was it the first tx in the block, no rollback needed
			if index == 0 {
				return err
			}
			fundsStateChangeRollback(txSlice[0 : index-1])
			return err
		}

		//we're manipulating pointer, no need to write back
		accSender.TxCnt += 1
		accSender.Balance -= tx.Amount
		accReceiver.Balance += tx.Amount
	}

	return nil
}

//we accept config slices with unknown id, but don't act on the payload
func configStateChange(configTxSlice []*protocol.ConfigTx, blockHash [32]byte) {

	var newParameters parameters
	//initialize it to state right now (before validating config txs)
	newParameters = *activeParameters

	if len(configTxSlice) == 0 {
		return
	}
	var change bool
	for _, tx := range configTxSlice {
		switch tx.Id {
		case protocol.FEE_MINIMUM_ID:
			if parameterBoundsChecking(protocol.FEE_MINIMUM_ID, tx.Payload) {
				newParameters.fee_minimum = tx.Payload
				change = true
			}
		case protocol.BLOCK_SIZE_ID:
			if parameterBoundsChecking(protocol.BLOCK_SIZE_ID, tx.Payload) {
				newParameters.block_size = tx.Payload
				change = true
			}
		case protocol.DIFF_INTERVAL_ID:
			if parameterBoundsChecking(protocol.DIFF_INTERVAL_ID, tx.Payload) {
				newParameters.diff_interval = tx.Payload
				change = true
			}
		case protocol.BLOCK_INTERVAL_ID:
			if parameterBoundsChecking(protocol.BLOCK_INTERVAL_ID, tx.Payload) {
				newParameters.block_interval = tx.Payload
				change = true
			}
		case protocol.BLOCK_REWARD_ID:
			if parameterBoundsChecking(protocol.BLOCK_REWARD_ID, tx.Payload) {
				newParameters.block_reward = tx.Payload
				change = true
			}
		}
	}

	//only add a new parameter struct if something meaningful actually changed
	if change {
		parameterSlice = append(parameterSlice, newParameters)
		activeParameters = &parameterSlice[len(parameterSlice)-1]
	}
}

func collectTxFees(accTxSlice []*protocol.AccTx, fundsTxSlice []*protocol.FundsTx, configTxSlice []*protocol.ConfigTx, minerHash [32]byte) error {

	var tmpAccTx []*protocol.AccTx
	var tmpFundsTx []*protocol.FundsTx
	var tmpConfigTx []*protocol.ConfigTx

	minerAcc := storage.GetAccountFromHash(minerHash)

	for _, tx := range accTxSlice {
		if minerAcc.Balance+tx.Fee > protocol.MAX_MONEY {
			//rollback of all perviously transferred transaction fees to the protocol's account
			collectTxFeesRollback(tmpAccTx, tmpFundsTx, tmpConfigTx, minerHash)
			logger.Printf("Miner balance (%v) overflows with transaction fee (%v).\n", minerAcc.Balance, tx.Fee)
			return errors.New("Miner balance overflows with transaction fee.\n")
		}

		//money gets created from thin air
		//no need to subtract money from root key
		minerAcc.Balance += tx.Fee
		tmpAccTx = append(tmpAccTx, tx)
	}

	//subtract fees from sender (check if that is allowed has already been done in the block validation)
	for _, tx := range fundsTxSlice {
		//preventing protocol account from overflowing
		if minerAcc.Balance+tx.Fee > protocol.MAX_MONEY {
			//rollback of all perviously transferred transaction fees to the protocol's account
			collectTxFeesRollback(tmpAccTx, tmpFundsTx, tmpConfigTx, minerHash)
			logger.Printf("Miner balance (%v) overflows with transaction fee (%v).\n", minerAcc.Balance, tx.Fee)
			return errors.New("Miner balance overflows with transaction fee.\n")
		}
		minerAcc.Balance += tx.Fee

		senderAcc := storage.GetAccountFromHash(tx.From)
		senderAcc.Balance -= tx.Fee

		tmpFundsTx = append(tmpFundsTx, tx)
	}

	for _, tx := range configTxSlice {
		if minerAcc.Balance+tx.Fee > protocol.MAX_MONEY {
			//rollback of all perviously transferred transaction fees to the protocol's account
			collectTxFeesRollback(tmpAccTx, tmpFundsTx, tmpConfigTx, minerHash)
			logger.Printf("Miner balance (%v) overflows with transaction fee (%v).\n", minerAcc.Balance, tx.Fee)
			return errors.New("Miner balance overflows with transaction fee.\n")
		}
		minerAcc.Balance += tx.Fee
		tmpConfigTx = append(tmpConfigTx, tx)
	}

	return nil
}

func collectBlockReward(reward uint64, minerHash [32]byte) error {
	miner := storage.GetAccountFromHash(minerHash)

	if miner == nil {
		return errors.New("Miner doesn't exist in the state!")
	}

	if miner.Balance+reward > protocol.MAX_MONEY {
		logger.Printf("Miner balance (%v) overflows with block reward (%v).\n", miner.Balance, reward)
		return errors.New("Miner balance overflows with transaction fee.\n")
	}
	miner.Balance += reward
	return nil
}

func printState() {
	logger.Println("State updated: ")
	for key, acc := range storage.State {
		logger.Printf("%x: %v\n", key[0:10], acc)
	}
}
<commit_msg>Bug fix, forgot to include the blockhahsh in the parameter update structure.<commit_after>package miner

import (
	"errors"
	"github.com/lisgie/bazo_miner/protocol"
	"github.com/lisgie/bazo_miner/storage"
	"golang.org/x/crypto/sha3"
)

func isRootKey(hash [32]byte) bool {
	_, exists := storage.RootKeys[hash]
	return exists
}

//for normal accounts, it
func accStateChange(txSlice []*protocol.AccTx) error {

	for _, tx := range txSlice {
		switch tx.Header {
		case 1:
			//first bit set, given account will be a new root account
			newAcc := protocol.Account{Address: tx.PubKey}
			storage.RootKeys[sha3.Sum256(tx.PubKey[:])] = &newAcc
			continue
		case 2:
			//second bit set, delete account from root account
			delete(storage.RootKeys, sha3.Sum256(tx.PubKey[:]))
			continue
		}

		//create a regular account
		addressHash := sha3.Sum256(tx.PubKey[:])
		acc := storage.GetAccountFromHash(addressHash)
		if acc != nil {
			logger.Printf("CRITICAL: Address already exists in the state: %x\n", addressHash[0:4])
			return errors.New("CRITICAL: Address already exists in the state")
		}
		newAcc := protocol.Account{Address: tx.PubKey}
		storage.State[addressHash] = &newAcc
	}
	return nil
}

func fundsStateChange(txSlice []*protocol.FundsTx) error {

	for index, tx := range txSlice {

		var err error
		//check if we have to issue new coins
		for hash, rootAcc := range storage.RootKeys {
			if hash == tx.From {
				logger.Printf("Root Key Transaction: %x\n", hash[0:8])

				if rootAcc.Balance+tx.Amount+tx.Fee > protocol.MAX_MONEY {
					logger.Printf("Root Account overflows (%v) with given transaction amount (%v) and fee (%v).\n", rootAcc.Balance, tx.Amount, tx.Fee)
					err = errors.New("Sender does not exist in the State.")
				}

				rootAcc.Balance += tx.Amount
				rootAcc.Balance += tx.Fee
			}
		}

		accSender, accReceiver := storage.GetAccountFromHash(tx.From), storage.GetAccountFromHash(tx.To)
		if accSender == nil {
			logger.Printf("CRITICAL: Sender does not exist in the State: %x\n", tx.From[0:8])
			err = errors.New("Sender does not exist in the State.")
		}

		if accReceiver == nil {
			logger.Printf("CRITICAL: Receiver does not exist in the State: %x\n", tx.To[0:8])
			err = errors.New("Receiver does not exist in the State.")
		}

		//also check for txCnt
		if tx.TxCnt != accSender.TxCnt {
			logger.Printf("Sender txCnt does not match: %v (tx.txCnt) vs. %v (state txCnt)\n", tx.TxCnt, accSender.TxCnt)
			err = errors.New("TxCnt mismatch!")
		}

		if (tx.Amount + tx.Fee) > accSender.Balance {
			logger.Printf("Sender does not have enough balance: %x\n", accSender.Balance)
			err = errors.New("Sender does not have enough funds for the transaction.")
		}

		//overflow protection
		if tx.Amount+accReceiver.Balance > protocol.MAX_MONEY {
			logger.Printf("Transaction amount (%v) would lead to balance overflow at the receiver account (%v)\n", tx.Amount, accReceiver.Balance)
			err = errors.New("Transaction amount would lead to balance overflow at the receiver account\n")
		}

		if err != nil {
			//was it the first tx in the block, no rollback needed
			if index == 0 {
				return err
			}
			fundsStateChangeRollback(txSlice[0 : index-1])
			return err
		}

		//we're manipulating pointer, no need to write back
		accSender.TxCnt += 1
		accSender.Balance -= tx.Amount
		accReceiver.Balance += tx.Amount
	}

	return nil
}

//we accept config slices with unknown id, but don't act on the payload
func configStateChange(configTxSlice []*protocol.ConfigTx, blockHash [32]byte) {

	var newParameters parameters
	//initialize it to state right now (before validating config txs)
	newParameters = *activeParameters

	if len(configTxSlice) == 0 {
		return
	}
	var change bool
	for _, tx := range configTxSlice {
		switch tx.Id {
		case protocol.FEE_MINIMUM_ID:
			if parameterBoundsChecking(protocol.FEE_MINIMUM_ID, tx.Payload) {
				newParameters.fee_minimum = tx.Payload
				change = true
			}
		case protocol.BLOCK_SIZE_ID:
			if parameterBoundsChecking(protocol.BLOCK_SIZE_ID, tx.Payload) {
				newParameters.block_size = tx.Payload
				change = true
			}
		case protocol.DIFF_INTERVAL_ID:
			if parameterBoundsChecking(protocol.DIFF_INTERVAL_ID, tx.Payload) {
				newParameters.diff_interval = tx.Payload
				change = true
			}
		case protocol.BLOCK_INTERVAL_ID:
			if parameterBoundsChecking(protocol.BLOCK_INTERVAL_ID, tx.Payload) {
				newParameters.block_interval = tx.Payload
				change = true
			}
		case protocol.BLOCK_REWARD_ID:
			if parameterBoundsChecking(protocol.BLOCK_REWARD_ID, tx.Payload) {
				newParameters.block_reward = tx.Payload
				change = true
			}
		}
	}

	//only add a new parameter struct if something meaningful actually changed
	if change {
		newParameters.blockHash = blockHash
		parameterSlice = append(parameterSlice, newParameters)
		activeParameters = &parameterSlice[len(parameterSlice)-1]
	}
}

func collectTxFees(accTxSlice []*protocol.AccTx, fundsTxSlice []*protocol.FundsTx, configTxSlice []*protocol.ConfigTx, minerHash [32]byte) error {

	var tmpAccTx []*protocol.AccTx
	var tmpFundsTx []*protocol.FundsTx
	var tmpConfigTx []*protocol.ConfigTx

	minerAcc := storage.GetAccountFromHash(minerHash)

	for _, tx := range accTxSlice {
		if minerAcc.Balance+tx.Fee > protocol.MAX_MONEY {
			//rollback of all perviously transferred transaction fees to the protocol's account
			collectTxFeesRollback(tmpAccTx, tmpFundsTx, tmpConfigTx, minerHash)
			logger.Printf("Miner balance (%v) overflows with transaction fee (%v).\n", minerAcc.Balance, tx.Fee)
			return errors.New("Miner balance overflows with transaction fee.\n")
		}

		//money gets created from thin air
		//no need to subtract money from root key
		minerAcc.Balance += tx.Fee
		tmpAccTx = append(tmpAccTx, tx)
	}

	//subtract fees from sender (check if that is allowed has already been done in the block validation)
	for _, tx := range fundsTxSlice {
		//preventing protocol account from overflowing
		if minerAcc.Balance+tx.Fee > protocol.MAX_MONEY {
			//rollback of all perviously transferred transaction fees to the protocol's account
			collectTxFeesRollback(tmpAccTx, tmpFundsTx, tmpConfigTx, minerHash)
			logger.Printf("Miner balance (%v) overflows with transaction fee (%v).\n", minerAcc.Balance, tx.Fee)
			return errors.New("Miner balance overflows with transaction fee.\n")
		}
		minerAcc.Balance += tx.Fee

		senderAcc := storage.GetAccountFromHash(tx.From)
		senderAcc.Balance -= tx.Fee

		tmpFundsTx = append(tmpFundsTx, tx)
	}

	for _, tx := range configTxSlice {
		if minerAcc.Balance+tx.Fee > protocol.MAX_MONEY {
			//rollback of all perviously transferred transaction fees to the protocol's account
			collectTxFeesRollback(tmpAccTx, tmpFundsTx, tmpConfigTx, minerHash)
			logger.Printf("Miner balance (%v) overflows with transaction fee (%v).\n", minerAcc.Balance, tx.Fee)
			return errors.New("Miner balance overflows with transaction fee.\n")
		}
		minerAcc.Balance += tx.Fee
		tmpConfigTx = append(tmpConfigTx, tx)
	}

	return nil
}

func collectBlockReward(reward uint64, minerHash [32]byte) error {
	miner := storage.GetAccountFromHash(minerHash)

	if miner == nil {
		return errors.New("Miner doesn't exist in the state!")
	}

	if miner.Balance+reward > protocol.MAX_MONEY {
		logger.Printf("Miner balance (%v) overflows with block reward (%v).\n", miner.Balance, reward)
		return errors.New("Miner balance overflows with transaction fee.\n")
	}
	miner.Balance += reward
	return nil
}

func printState() {
	logger.Println("State updated: ")
	for key, acc := range storage.State {
		logger.Printf("%x: %v\n", key[0:10], acc)
	}
}
<|endoftext|>


<commit_before>// © 2012 the Minima Authors under the MIT license. See AUTHORS for the list of authors.

package main

import (
	"code.google.com/p/min-game/geom"
	"code.google.com/p/min-game/item"
	"code.google.com/p/min-game/ui"
)

type Base struct {
	Box geom.Rectangle

	Storage Inventory
}

func NewBase(p geom.Point) Base {
	return Base{
		Box: geom.Rectangle{
			Min: p,
			Max: p.Add(geom.Pt(64, 64)),
		},
		Storage: Inventory{[]*item.Item{item.New(item.ETele)}, 0, false},
	}
}

func (b *Base) Draw(d ui.Drawer, cam ui.Camera) {
	cam.Draw(d, ui.Sprite{
		Name:   "Base",
		Bounds: geom.Rect(0, 0, b.Box.Dx(), b.Box.Dy()),
		Shade:  1.0,
	}, b.Box.Min)
}

// PutStorage adds i to the base's storage.
func (b *Base) PutStorage(i *item.Item) {
	b.Storage.Put(i)
}

type BaseScreen struct {
	astro   *Player
	base    *Base
	closing bool

	inPack   bool
	selected int
}

const pad = 4

var origin = geom.Pt(32, 32)
var bounds = geom.Rectangle{
	Min: origin,
	Max: origin.Add(geom.Pt(ScreenDims.X, ScreenDims.Y/2)).Sub(origin.Mul(2)),
}
var packBounds = bounds.Add(geom.Pt(0, bounds.Dy()+3*pad+32))

func NewBaseScreen(astro *Player, base *Base) *BaseScreen {
	return &BaseScreen{astro, base, false, false, 0}
}

func (s *BaseScreen) Transparent() bool {
	return true
}

func (s *BaseScreen) Draw(d ui.Drawer) {
	d.SetFont(DialogFont, 16)
	pt := DrawInventory(BaseInv{s, "Pack"}, d, pad, origin, true)
	DrawInventory(BaseInv{s, "Storage"}, d, pad, geom.Pt(origin.X, pt.Y+32+2*pad), false)
}

type BaseInv struct {
	s     *BaseScreen
	label string
}

func (b BaseInv) Label() string {
	return b.label
}

func (b BaseInv) Len() int {
	if b.label == "Pack" {
		return b.s.astro.pack.Len()
	}
	return b.s.base.Storage.Len()
}

func (b BaseInv) Selected(n int) bool {
	if b.label == "Pack" && b.s.inPack || b.label == "Storage" && !b.s.inPack {
		return b.s.selected == n
	}
	return false
}

func (b BaseInv) Get(n int) *item.Item {
	if b.label == "Pack" {
		return b.s.astro.pack.Get(n)
	}
	return b.s.base.Storage.Get(n)
}

func (b BaseInv) Set(n int, i *item.Item) {
	if b.label == "Pack" {
		b.s.astro.pack.Set(n, i)
	} else {
		b.s.base.Storage.Set(n, i)
	}
}

func (s *BaseScreen) Handle(stk *ui.ScreenStack, e ui.Event) error {
	if s.closing {
		return nil
	}

	key, ok := e.(ui.Key)
	if !ok || !key.Down {
		return nil
	}

	switch key.Button {
	case ui.Menu:
		s.closing = true
	case ui.Action:
		if s.inPack && s.astro.pack.Get(s.selected) != nil {
			i := s.astro.pack.Get(s.selected)
			s.astro.pack.Set(s.selected, nil)
			s.base.PutStorage(i)
		}
		if !s.inPack && s.base.Storage.Get(s.selected) != nil {
			i := s.base.Storage.Get(s.selected)
			if s.astro.PutPack(i) {
				s.base.Storage.Set(s.selected, nil)
			}
		}
	case ui.Left:
		s.selected--
		if s.selected < 0 {
			if s.inPack {
				s.selected = s.astro.pack.Len() - 1
			} else {
				s.selected = s.base.Storage.Len() - 1
			}
		}
	case ui.Right:
		s.selected++
		if s.inPack && s.selected == s.astro.pack.Len() {
			s.selected = 0
		}
		if !s.inPack && s.selected == s.base.Storage.Len() {
			s.selected = 0
		}
	case ui.Up, ui.Down:
		s.inPack = !s.inPack
		if s.inPack && s.selected >= s.astro.pack.Len() {
			s.selected = s.astro.pack.Len() - 1
		}
		if !s.inPack && s.selected >= s.base.Storage.Len() {
			s.selected = s.base.Storage.Len() - 1
		}
	}
	return nil
}

func (s *BaseScreen) Update(stk *ui.ScreenStack) error {
	s.astro.RefillO2()

	if s.closing {
		stk.Pop()
	}
	return nil
}
<commit_msg>Use the Inventory's Storage field in the base, instead of a custom one.<commit_after>// © 2012 the Minima Authors under the MIT license. See AUTHORS for the list of authors.

package main

import (
	"code.google.com/p/min-game/geom"
	"code.google.com/p/min-game/item"
	"code.google.com/p/min-game/ui"
)

type Base struct {
	Box geom.Rectangle

	Storage Inventory
}

func NewBase(p geom.Point) Base {
	return Base{
		Box: geom.Rectangle{
			Min: p,
			Max: p.Add(geom.Pt(64, 64)),
		},
		Storage: Inventory{[]*item.Item{item.New(item.ETele)}, 0, false},
	}
}

func (b *Base) Draw(d ui.Drawer, cam ui.Camera) {
	cam.Draw(d, ui.Sprite{
		Name:   "Base",
		Bounds: geom.Rect(0, 0, b.Box.Dx(), b.Box.Dy()),
		Shade:  1.0,
	}, b.Box.Min)
}

// PutStorage adds i to the base's storage.
func (b *Base) PutStorage(i *item.Item) {
	b.Storage.Put(i)
}

type BaseScreen struct {
	astro   *Player
	base    *Base
	closing bool

	inPack bool
}

const pad = 4

var origin = geom.Pt(32, 32)
var bounds = geom.Rectangle{
	Min: origin,
	Max: origin.Add(geom.Pt(ScreenDims.X, ScreenDims.Y/2)).Sub(origin.Mul(2)),
}
var packBounds = bounds.Add(geom.Pt(0, bounds.Dy()+3*pad+32))

func NewBaseScreen(astro *Player, base *Base) *BaseScreen {
	return &BaseScreen{astro, base, false, false}
}

func (s *BaseScreen) Transparent() bool {
	return true
}

func (s *BaseScreen) Draw(d ui.Drawer) {
	d.SetFont(DialogFont, 16)
	pt := s.astro.pack.Draw("Pack", d, pad, origin, true)
	s.base.Storage.Draw("Storage", d, pad, geom.Pt(origin.X, pt.Y+32+2*pad), false)
}

func (s *BaseScreen) Handle(stk *ui.ScreenStack, e ui.Event) error {
	if s.closing {
		return nil
	}

	key, ok := e.(ui.Key)
	if !ok || !key.Down {
		return nil
	}

	switch key.Button {
	case ui.Menu:
		s.closing = true
	case ui.Action:
		if s.inPack && s.astro.pack.Get(s.astro.pack.Selected) != nil {
			i := s.astro.pack.Get(s.astro.pack.Selected)
			s.astro.pack.Set(s.astro.pack.Selected, nil)
			s.base.PutStorage(i)
		}
		if !s.inPack && s.base.Storage.Get(s.base.Storage.Selected) != nil {
			i := s.base.Storage.Get(s.base.Storage.Selected)
			if s.astro.PutPack(i) {
				s.base.Storage.Set(s.base.Storage.Selected, nil)
			}
		}
	case ui.Left:
		if s.inPack {
			s.astro.pack.Selected--
			if s.astro.pack.Selected < 0 {
				s.astro.pack.Selected = s.astro.pack.Len() - 1
			}
		} else {
			s.base.Storage.Selected--
			if s.base.Storage.Selected < 0 {
				s.astro.pack.Selected = s.base.Storage.Len() - 1
			}
		}
	case ui.Right:
		if s.inPack {
			s.astro.pack.Selected++
			if s.astro.pack.Selected == s.astro.pack.Len() {
				s.astro.pack.Selected = 0
			}
		} else {
			s.base.Storage.Selected++
			if s.base.Storage.Selected == s.base.Storage.Len() {
				s.base.Storage.Selected = 0
			}
		}
	case ui.Up, ui.Down:
		if s.inPack {
			if s.base.Storage.Selected >= s.astro.pack.Len() {
				s.astro.pack.Selected = s.astro.pack.Len() - 1
			}
			s.inPack = false
		} else {
			if s.astro.pack.Selected >= s.base.Storage.Len() {
				s.base.Storage.Selected = s.base.Storage.Len() - 1
			}
			s.inPack = true
		}
	}
	return nil
}

func (s *BaseScreen) Update(stk *ui.ScreenStack) error {
	s.astro.RefillO2()

	if s.closing {
		stk.Pop()
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package action

import (
	"encoding/json"
	"fmt"
	"path"
	"strings"

	"code.gitea.io/gitea/models"
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/notification/base"
	"code.gitea.io/gitea/modules/repository"
)

type actionNotifier struct {
	base.NullNotifier
}

var (
	_ base.Notifier = &actionNotifier{}
)

// NewNotifier create a new actionNotifier notifier
func NewNotifier() base.Notifier {
	return &actionNotifier{}
}

func (a *actionNotifier) NotifyNewIssue(issue *models.Issue) {
	if err := issue.LoadPoster(); err != nil {
		log.Error("issue.LoadPoster: %v", err)
		return
	}
	if err := issue.LoadRepo(); err != nil {
		log.Error("issue.LoadRepo: %v", err)
		return
	}
	repo := issue.Repo

	if err := models.NotifyWatchers(&models.Action{
		ActUserID: issue.Poster.ID,
		ActUser:   issue.Poster,
		OpType:    models.ActionCreateIssue,
		Content:   fmt.Sprintf("%d|%s", issue.Index, issue.Title),
		RepoID:    repo.ID,
		Repo:      repo,
		IsPrivate: repo.IsPrivate,
	}); err != nil {
		log.Error("NotifyWatchers: %v", err)
	}
}

// NotifyIssueChangeStatus notifies close or reopen issue to notifiers
func (a *actionNotifier) NotifyIssueChangeStatus(doer *models.User, issue *models.Issue, actionComment *models.Comment, closeOrReopen bool) {
	// Compose comment action, could be plain comment, close or reopen issue/pull request.
	// This object will be used to notify watchers in the end of function.
	act := &models.Action{
		ActUserID: doer.ID,
		ActUser:   doer,
		Content:   fmt.Sprintf("%d|%s", issue.Index, ""),
		RepoID:    issue.Repo.ID,
		Repo:      issue.Repo,
		Comment:   actionComment,
		CommentID: actionComment.ID,
		IsPrivate: issue.Repo.IsPrivate,
	}
	// Check comment type.
	if closeOrReopen {
		act.OpType = models.ActionCloseIssue
		if issue.IsPull {
			act.OpType = models.ActionClosePullRequest
		}
	} else {
		act.OpType = models.ActionReopenIssue
		if issue.IsPull {
			act.OpType = models.ActionReopenPullRequest
		}
	}

	// Notify watchers for whatever action comes in, ignore if no action type.
	if err := models.NotifyWatchers(act); err != nil {
		log.Error("NotifyWatchers: %v", err)
	}
}

// NotifyCreateIssueComment notifies comment on an issue to notifiers
func (a *actionNotifier) NotifyCreateIssueComment(doer *models.User, repo *models.Repository,
	issue *models.Issue, comment *models.Comment) {
	act := &models.Action{
		ActUserID: doer.ID,
		ActUser:   doer,
		RepoID:    issue.Repo.ID,
		Repo:      issue.Repo,
		Comment:   comment,
		CommentID: comment.ID,
		IsPrivate: issue.Repo.IsPrivate,
	}

	content := ""

	if len(comment.Content) > 200 {
		content = comment.Content[:strings.LastIndex(comment.Content[0:200], " ")] + "…"
	} else {
		content = comment.Content
	}
	act.Content = fmt.Sprintf("%d|%s", issue.Index, content)

	if issue.IsPull {
		act.OpType = models.ActionCommentPull
	} else {
		act.OpType = models.ActionCommentIssue
	}

	// Notify watchers for whatever action comes in, ignore if no action type.
	if err := models.NotifyWatchers(act); err != nil {
		log.Error("NotifyWatchers: %v", err)
	}
}

func (a *actionNotifier) NotifyNewPullRequest(pull *models.PullRequest) {
	if err := pull.LoadIssue(); err != nil {
		log.Error("pull.LoadIssue: %v", err)
		return
	}
	if err := pull.Issue.LoadRepo(); err != nil {
		log.Error("pull.Issue.LoadRepo: %v", err)
		return
	}
	if err := pull.Issue.LoadPoster(); err != nil {
		log.Error("pull.Issue.LoadPoster: %v", err)
		return
	}

	if err := models.NotifyWatchers(&models.Action{
		ActUserID: pull.Issue.Poster.ID,
		ActUser:   pull.Issue.Poster,
		OpType:    models.ActionCreatePullRequest,
		Content:   fmt.Sprintf("%d|%s", pull.Issue.Index, pull.Issue.Title),
		RepoID:    pull.Issue.Repo.ID,
		Repo:      pull.Issue.Repo,
		IsPrivate: pull.Issue.Repo.IsPrivate,
	}); err != nil {
		log.Error("NotifyWatchers: %v", err)
	}
}

func (a *actionNotifier) NotifyRenameRepository(doer *models.User, repo *models.Repository, oldRepoName string) {
	log.Trace("action.ChangeRepositoryName: %s/%s", doer.Name, repo.Name)

	if err := models.NotifyWatchers(&models.Action{
		ActUserID: doer.ID,
		ActUser:   doer,
		OpType:    models.ActionRenameRepo,
		RepoID:    repo.ID,
		Repo:      repo,
		IsPrivate: repo.IsPrivate,
		Content:   oldRepoName,
	}); err != nil {
		log.Error("NotifyWatchers: %v", err)
	}
}

func (a *actionNotifier) NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string) {
	if err := models.NotifyWatchers(&models.Action{
		ActUserID: doer.ID,
		ActUser:   doer,
		OpType:    models.ActionTransferRepo,
		RepoID:    repo.ID,
		Repo:      repo,
		IsPrivate: repo.IsPrivate,
		Content:   path.Join(oldOwnerName, repo.Name),
	}); err != nil {
		log.Error("NotifyWatchers: %v", err)
	}
}

func (a *actionNotifier) NotifyCreateRepository(doer *models.User, u *models.User, repo *models.Repository) {
	if err := models.NotifyWatchers(&models.Action{
		ActUserID: doer.ID,
		ActUser:   doer,
		OpType:    models.ActionCreateRepo,
		RepoID:    repo.ID,
		Repo:      repo,
		IsPrivate: repo.IsPrivate,
	}); err != nil {
		log.Error("notify watchers '%d/%d': %v", doer.ID, repo.ID, err)
	}
}

func (a *actionNotifier) NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository) {
	if err := models.NotifyWatchers(&models.Action{
		ActUserID: doer.ID,
		ActUser:   doer,
		OpType:    models.ActionCreateRepo,
		RepoID:    repo.ID,
		Repo:      repo,
		IsPrivate: repo.IsPrivate,
	}); err != nil {
		log.Error("notify watchers '%d/%d': %v", doer.ID, repo.ID, err)
	}
}

func (a *actionNotifier) NotifyPullRequestReview(pr *models.PullRequest, review *models.Review, comment *models.Comment) {
	if err := review.LoadReviewer(); err != nil {
		log.Error("LoadReviewer '%d/%d': %v", review.ID, review.ReviewerID, err)
		return
	}
	if err := review.LoadCodeComments(); err != nil {
		log.Error("LoadCodeComments '%d/%d': %v", review.Reviewer.ID, review.ID, err)
		return
	}

	var actions = make([]*models.Action, 0, 10)
	for _, lines := range review.CodeComments {
		for _, comments := range lines {
			for _, comm := range comments {
				actions = append(actions, &models.Action{
					ActUserID: review.Reviewer.ID,
					ActUser:   review.Reviewer,
					Content:   fmt.Sprintf("%d|%s", review.Issue.Index, strings.Split(comm.Content, "\n")[0]),
					OpType:    models.ActionCommentPull,
					RepoID:    review.Issue.RepoID,
					Repo:      review.Issue.Repo,
					IsPrivate: review.Issue.Repo.IsPrivate,
					Comment:   comm,
					CommentID: comm.ID,
				})
			}
		}
	}

	if review.Type != models.ReviewTypeComment || strings.TrimSpace(comment.Content) != "" {
		action := &models.Action{
			ActUserID: review.Reviewer.ID,
			ActUser:   review.Reviewer,
			Content:   fmt.Sprintf("%d|%s", review.Issue.Index, strings.Split(comment.Content, "\n")[0]),
			RepoID:    review.Issue.RepoID,
			Repo:      review.Issue.Repo,
			IsPrivate: review.Issue.Repo.IsPrivate,
			Comment:   comment,
			CommentID: comment.ID,
		}

		switch review.Type {
		case models.ReviewTypeApprove:
			action.OpType = models.ActionApprovePullRequest
		case models.ReviewTypeReject:
			action.OpType = models.ActionRejectPullRequest
		default:
			action.OpType = models.ActionCommentPull
		}

		actions = append(actions, action)
	}

	if err := models.NotifyWatchersActions(actions); err != nil {
		log.Error("notify watchers '%d/%d': %v", review.Reviewer.ID, review.Issue.RepoID, err)
	}
}

func (*actionNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *models.User) {
	if err := models.NotifyWatchers(&models.Action{
		ActUserID: doer.ID,
		ActUser:   doer,
		OpType:    models.ActionMergePullRequest,
		Content:   fmt.Sprintf("%d|%s", pr.Issue.Index, pr.Issue.Title),
		RepoID:    pr.Issue.Repo.ID,
		Repo:      pr.Issue.Repo,
		IsPrivate: pr.Issue.Repo.IsPrivate,
	}); err != nil {
		log.Error("NotifyWatchers [%d]: %v", pr.ID, err)
	}
}

func (a *actionNotifier) NotifySyncPushCommits(pusher *models.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
	data, err := json.Marshal(commits)
	if err != nil {
		log.Error("json.Marshal: %v", err)
		return
	}

	if err := models.NotifyWatchers(&models.Action{
		ActUserID: repo.OwnerID,
		ActUser:   repo.MustOwner(),
		OpType:    models.ActionMirrorSyncPush,
		RepoID:    repo.ID,
		Repo:      repo,
		IsPrivate: repo.IsPrivate,
		RefName:   opts.RefFullName,
		Content:   string(data),
	}); err != nil {
		log.Error("notifyWatchers: %v", err)
	}
}

func (a *actionNotifier) NotifySyncCreateRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
	if err := models.NotifyWatchers(&models.Action{
		ActUserID: repo.OwnerID,
		ActUser:   repo.MustOwner(),
		OpType:    models.ActionMirrorSyncCreate,
		RepoID:    repo.ID,
		Repo:      repo,
		IsPrivate: repo.IsPrivate,
		RefName:   refFullName,
	}); err != nil {
		log.Error("notifyWatchers: %v", err)
	}
}

func (a *actionNotifier) NotifySyncDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
	if err := models.NotifyWatchers(&models.Action{
		ActUserID: repo.OwnerID,
		ActUser:   repo.MustOwner(),
		OpType:    models.ActionMirrorSyncCreate,
		RepoID:    repo.ID,
		Repo:      repo,
		IsPrivate: repo.IsPrivate,
		RefName:   refFullName,
	}); err != nil {
		log.Error("notifyWatchers: %v", err)
	}
}

func (a *actionNotifier) NotifyNewRelease(rel *models.Release) {
	if err := rel.LoadAttributes(); err != nil {
		log.Error("NotifyNewRelease: %v", err)
		return
	}
	if err := models.NotifyWatchers(&models.Action{
		ActUserID: rel.PublisherID,
		ActUser:   rel.Publisher,
		OpType:    models.ActionPublishRelease,
		RepoID:    rel.RepoID,
		Repo:      rel.Repo,
		IsPrivate: rel.Repo.IsPrivate,
		Content:   rel.Title,
		RefName:   rel.TagName,
	}); err != nil {
		log.Error("notifyWatchers: %v", err)
	}
}
<commit_msg>Fix typo (#13380)<commit_after>// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package action

import (
	"encoding/json"
	"fmt"
	"path"
	"strings"

	"code.gitea.io/gitea/models"
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/notification/base"
	"code.gitea.io/gitea/modules/repository"
)

type actionNotifier struct {
	base.NullNotifier
}

var (
	_ base.Notifier = &actionNotifier{}
)

// NewNotifier create a new actionNotifier notifier
func NewNotifier() base.Notifier {
	return &actionNotifier{}
}

func (a *actionNotifier) NotifyNewIssue(issue *models.Issue) {
	if err := issue.LoadPoster(); err != nil {
		log.Error("issue.LoadPoster: %v", err)
		return
	}
	if err := issue.LoadRepo(); err != nil {
		log.Error("issue.LoadRepo: %v", err)
		return
	}
	repo := issue.Repo

	if err := models.NotifyWatchers(&models.Action{
		ActUserID: issue.Poster.ID,
		ActUser:   issue.Poster,
		OpType:    models.ActionCreateIssue,
		Content:   fmt.Sprintf("%d|%s", issue.Index, issue.Title),
		RepoID:    repo.ID,
		Repo:      repo,
		IsPrivate: repo.IsPrivate,
	}); err != nil {
		log.Error("NotifyWatchers: %v", err)
	}
}

// NotifyIssueChangeStatus notifies close or reopen issue to notifiers
func (a *actionNotifier) NotifyIssueChangeStatus(doer *models.User, issue *models.Issue, actionComment *models.Comment, closeOrReopen bool) {
	// Compose comment action, could be plain comment, close or reopen issue/pull request.
	// This object will be used to notify watchers in the end of function.
	act := &models.Action{
		ActUserID: doer.ID,
		ActUser:   doer,
		Content:   fmt.Sprintf("%d|%s", issue.Index, ""),
		RepoID:    issue.Repo.ID,
		Repo:      issue.Repo,
		Comment:   actionComment,
		CommentID: actionComment.ID,
		IsPrivate: issue.Repo.IsPrivate,
	}
	// Check comment type.
	if closeOrReopen {
		act.OpType = models.ActionCloseIssue
		if issue.IsPull {
			act.OpType = models.ActionClosePullRequest
		}
	} else {
		act.OpType = models.ActionReopenIssue
		if issue.IsPull {
			act.OpType = models.ActionReopenPullRequest
		}
	}

	// Notify watchers for whatever action comes in, ignore if no action type.
	if err := models.NotifyWatchers(act); err != nil {
		log.Error("NotifyWatchers: %v", err)
	}
}

// NotifyCreateIssueComment notifies comment on an issue to notifiers
func (a *actionNotifier) NotifyCreateIssueComment(doer *models.User, repo *models.Repository,
	issue *models.Issue, comment *models.Comment) {
	act := &models.Action{
		ActUserID: doer.ID,
		ActUser:   doer,
		RepoID:    issue.Repo.ID,
		Repo:      issue.Repo,
		Comment:   comment,
		CommentID: comment.ID,
		IsPrivate: issue.Repo.IsPrivate,
	}

	content := ""

	if len(comment.Content) > 200 {
		content = comment.Content[:strings.LastIndex(comment.Content[0:200], " ")] + "…"
	} else {
		content = comment.Content
	}
	act.Content = fmt.Sprintf("%d|%s", issue.Index, content)

	if issue.IsPull {
		act.OpType = models.ActionCommentPull
	} else {
		act.OpType = models.ActionCommentIssue
	}

	// Notify watchers for whatever action comes in, ignore if no action type.
	if err := models.NotifyWatchers(act); err != nil {
		log.Error("NotifyWatchers: %v", err)
	}
}

func (a *actionNotifier) NotifyNewPullRequest(pull *models.PullRequest) {
	if err := pull.LoadIssue(); err != nil {
		log.Error("pull.LoadIssue: %v", err)
		return
	}
	if err := pull.Issue.LoadRepo(); err != nil {
		log.Error("pull.Issue.LoadRepo: %v", err)
		return
	}
	if err := pull.Issue.LoadPoster(); err != nil {
		log.Error("pull.Issue.LoadPoster: %v", err)
		return
	}

	if err := models.NotifyWatchers(&models.Action{
		ActUserID: pull.Issue.Poster.ID,
		ActUser:   pull.Issue.Poster,
		OpType:    models.ActionCreatePullRequest,
		Content:   fmt.Sprintf("%d|%s", pull.Issue.Index, pull.Issue.Title),
		RepoID:    pull.Issue.Repo.ID,
		Repo:      pull.Issue.Repo,
		IsPrivate: pull.Issue.Repo.IsPrivate,
	}); err != nil {
		log.Error("NotifyWatchers: %v", err)
	}
}

func (a *actionNotifier) NotifyRenameRepository(doer *models.User, repo *models.Repository, oldRepoName string) {
	log.Trace("action.ChangeRepositoryName: %s/%s", doer.Name, repo.Name)

	if err := models.NotifyWatchers(&models.Action{
		ActUserID: doer.ID,
		ActUser:   doer,
		OpType:    models.ActionRenameRepo,
		RepoID:    repo.ID,
		Repo:      repo,
		IsPrivate: repo.IsPrivate,
		Content:   oldRepoName,
	}); err != nil {
		log.Error("NotifyWatchers: %v", err)
	}
}

func (a *actionNotifier) NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string) {
	if err := models.NotifyWatchers(&models.Action{
		ActUserID: doer.ID,
		ActUser:   doer,
		OpType:    models.ActionTransferRepo,
		RepoID:    repo.ID,
		Repo:      repo,
		IsPrivate: repo.IsPrivate,
		Content:   path.Join(oldOwnerName, repo.Name),
	}); err != nil {
		log.Error("NotifyWatchers: %v", err)
	}
}

func (a *actionNotifier) NotifyCreateRepository(doer *models.User, u *models.User, repo *models.Repository) {
	if err := models.NotifyWatchers(&models.Action{
		ActUserID: doer.ID,
		ActUser:   doer,
		OpType:    models.ActionCreateRepo,
		RepoID:    repo.ID,
		Repo:      repo,
		IsPrivate: repo.IsPrivate,
	}); err != nil {
		log.Error("notify watchers '%d/%d': %v", doer.ID, repo.ID, err)
	}
}

func (a *actionNotifier) NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository) {
	if err := models.NotifyWatchers(&models.Action{
		ActUserID: doer.ID,
		ActUser:   doer,
		OpType:    models.ActionCreateRepo,
		RepoID:    repo.ID,
		Repo:      repo,
		IsPrivate: repo.IsPrivate,
	}); err != nil {
		log.Error("notify watchers '%d/%d': %v", doer.ID, repo.ID, err)
	}
}

func (a *actionNotifier) NotifyPullRequestReview(pr *models.PullRequest, review *models.Review, comment *models.Comment) {
	if err := review.LoadReviewer(); err != nil {
		log.Error("LoadReviewer '%d/%d': %v", review.ID, review.ReviewerID, err)
		return
	}
	if err := review.LoadCodeComments(); err != nil {
		log.Error("LoadCodeComments '%d/%d': %v", review.Reviewer.ID, review.ID, err)
		return
	}

	var actions = make([]*models.Action, 0, 10)
	for _, lines := range review.CodeComments {
		for _, comments := range lines {
			for _, comm := range comments {
				actions = append(actions, &models.Action{
					ActUserID: review.Reviewer.ID,
					ActUser:   review.Reviewer,
					Content:   fmt.Sprintf("%d|%s", review.Issue.Index, strings.Split(comm.Content, "\n")[0]),
					OpType:    models.ActionCommentPull,
					RepoID:    review.Issue.RepoID,
					Repo:      review.Issue.Repo,
					IsPrivate: review.Issue.Repo.IsPrivate,
					Comment:   comm,
					CommentID: comm.ID,
				})
			}
		}
	}

	if review.Type != models.ReviewTypeComment || strings.TrimSpace(comment.Content) != "" {
		action := &models.Action{
			ActUserID: review.Reviewer.ID,
			ActUser:   review.Reviewer,
			Content:   fmt.Sprintf("%d|%s", review.Issue.Index, strings.Split(comment.Content, "\n")[0]),
			RepoID:    review.Issue.RepoID,
			Repo:      review.Issue.Repo,
			IsPrivate: review.Issue.Repo.IsPrivate,
			Comment:   comment,
			CommentID: comment.ID,
		}

		switch review.Type {
		case models.ReviewTypeApprove:
			action.OpType = models.ActionApprovePullRequest
		case models.ReviewTypeReject:
			action.OpType = models.ActionRejectPullRequest
		default:
			action.OpType = models.ActionCommentPull
		}

		actions = append(actions, action)
	}

	if err := models.NotifyWatchersActions(actions); err != nil {
		log.Error("notify watchers '%d/%d': %v", review.Reviewer.ID, review.Issue.RepoID, err)
	}
}

func (*actionNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *models.User) {
	if err := models.NotifyWatchers(&models.Action{
		ActUserID: doer.ID,
		ActUser:   doer,
		OpType:    models.ActionMergePullRequest,
		Content:   fmt.Sprintf("%d|%s", pr.Issue.Index, pr.Issue.Title),
		RepoID:    pr.Issue.Repo.ID,
		Repo:      pr.Issue.Repo,
		IsPrivate: pr.Issue.Repo.IsPrivate,
	}); err != nil {
		log.Error("NotifyWatchers [%d]: %v", pr.ID, err)
	}
}

func (a *actionNotifier) NotifySyncPushCommits(pusher *models.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
	data, err := json.Marshal(commits)
	if err != nil {
		log.Error("json.Marshal: %v", err)
		return
	}

	if err := models.NotifyWatchers(&models.Action{
		ActUserID: repo.OwnerID,
		ActUser:   repo.MustOwner(),
		OpType:    models.ActionMirrorSyncPush,
		RepoID:    repo.ID,
		Repo:      repo,
		IsPrivate: repo.IsPrivate,
		RefName:   opts.RefFullName,
		Content:   string(data),
	}); err != nil {
		log.Error("notifyWatchers: %v", err)
	}
}

func (a *actionNotifier) NotifySyncCreateRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
	if err := models.NotifyWatchers(&models.Action{
		ActUserID: repo.OwnerID,
		ActUser:   repo.MustOwner(),
		OpType:    models.ActionMirrorSyncCreate,
		RepoID:    repo.ID,
		Repo:      repo,
		IsPrivate: repo.IsPrivate,
		RefName:   refFullName,
	}); err != nil {
		log.Error("notifyWatchers: %v", err)
	}
}

func (a *actionNotifier) NotifySyncDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
	if err := models.NotifyWatchers(&models.Action{
		ActUserID: repo.OwnerID,
		ActUser:   repo.MustOwner(),
		OpType:    models.ActionMirrorSyncDelete,
		RepoID:    repo.ID,
		Repo:      repo,
		IsPrivate: repo.IsPrivate,
		RefName:   refFullName,
	}); err != nil {
		log.Error("notifyWatchers: %v", err)
	}
}

func (a *actionNotifier) NotifyNewRelease(rel *models.Release) {
	if err := rel.LoadAttributes(); err != nil {
		log.Error("NotifyNewRelease: %v", err)
		return
	}
	if err := models.NotifyWatchers(&models.Action{
		ActUserID: rel.PublisherID,
		ActUser:   rel.Publisher,
		OpType:    models.ActionPublishRelease,
		RepoID:    rel.RepoID,
		Repo:      rel.Repo,
		IsPrivate: rel.Repo.IsPrivate,
		Content:   rel.Title,
		RefName:   rel.TagName,
	}); err != nil {
		log.Error("notifyWatchers: %v", err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package syscall

//sys	Chown(path string, uid int, gid int) (err error)
//sys	Fchown(fd int, uid int, gid int) (err error)
//sys	Fstat(fd int, stat *Stat_t) (err error)
//sys	Fstatfs(fd int, buf *Statfs_t) (err error)
//sys	Ftruncate(fd int, length int64) (err error)
//sysnb	Getegid() (egid int)
//sysnb	Geteuid() (euid int)
//sysnb	Getgid() (gid int)
//sysnb	Getrlimit(resource int, rlim *Rlimit) (err error)
//sysnb	Getuid() (uid int)
//sys	Ioperm(from int, num int, on int) (err error)
//sys	Iopl(level int) (err error)
//sys	Lchown(path string, uid int, gid int) (err error)
//sys	Listen(s int, n int) (err error)
//sys	Lstat(path string, stat *Stat_t) (err error)
//sys	Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64
//sys	Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64
//sys	Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK
//sys	Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error)
//sys	sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
//sys	Setfsgid(gid int) (err error)
//sys	Setfsuid(uid int) (err error)
//sysnb	Setgid(gid int) (err error)
//sysnb	Setregid(rgid int, egid int) (err error)
//sysnb	Setresgid(rgid int, egid int, sgid int) (err error)
//sysnb	Setresuid(ruid int, euid int, suid int) (err error)
//sysnb	Setrlimit(resource int, rlim *Rlimit) (err error)
//sysnb	Setreuid(ruid int, euid int) (err error)
//sys	Shutdown(fd int, how int) (err error)
//sys	Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error)
//sys	Stat(path string, stat *Stat_t) (err error)
//sys	Statfs(path string, buf *Statfs_t) (err error)
//sys	SyncFileRange(fd int, off int64, n int64, flags int) (err error)
//sys	Truncate(path string, length int64) (err error)
//sys	accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error)
//sys	accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error)
//sys	bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error)
//sys	connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error)
//sysnb	getgroups(n int, list *_Gid_t) (nn int, err error)
//sysnb	setgroups(n int, list *_Gid_t) (err error)
//sys	getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *_Socklen) (err error)
//sys	setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr) (err error)
//sysnb	socket(domain int, typ int, proto int) (fd int, err error)
//sysnb	socketpair(domain int, typ int, proto int, fd *[2]int32) (err error)
//sysnb	getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error)
//sysnb	getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error)
//sys	recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error)
//sys	sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (err error)
//sys	recvmsg(s int, msg *Msghdr, flags int) (n int, err error)
//sys	sendmsg(s int, msg *Msghdr, flags int) (n int, err error)
//sys	mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error)

func Getpagesize() int { return 4096 }

//go:noescape
func gettimeofday(tv *Timeval) (err Errno)

func Gettimeofday(tv *Timeval) (err error) {
	errno := gettimeofday(tv)
	if errno != 0 {
		return errno
	}
	return nil
}

func Time(t *Time_t) (tt Time_t, err error) {
	var tv Timeval
	errno := gettimeofday(&tv)
	if errno != 0 {
		return errno
	}
	if t != nil {
		*t = tv.Sec
	}
	return Time_t(tv.Sec), nil
}

func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }

func NsecToTimespec(nsec int64) (ts Timespec) {
	ts.Sec = nsec / 1e9
	ts.Nsec = nsec % 1e9
	return
}

func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }

func NsecToTimeval(nsec int64) (tv Timeval) {
	nsec += 999 // round up to microsecond
	tv.Sec = nsec / 1e9
	tv.Usec = nsec % 1e9 / 1e3
	return
}

func (r *PtraceRegs) PC() uint64 { return r.Rip }

func (r *PtraceRegs) SetPC(pc uint64) { r.Rip = pc }

func (iov *Iovec) SetLen(length int) {
	iov.Len = uint64(length)
}

func (msghdr *Msghdr) SetControllen(length int) {
	msghdr.Controllen = uint64(length)
}

func (cmsg *Cmsghdr) SetLen(length int) {
	cmsg.Len = uint64(length)
}
<commit_msg>syscall: fix linux amd64 build TBR=rsc<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package syscall

//sys	Chown(path string, uid int, gid int) (err error)
//sys	Fchown(fd int, uid int, gid int) (err error)
//sys	Fstat(fd int, stat *Stat_t) (err error)
//sys	Fstatfs(fd int, buf *Statfs_t) (err error)
//sys	Ftruncate(fd int, length int64) (err error)
//sysnb	Getegid() (egid int)
//sysnb	Geteuid() (euid int)
//sysnb	Getgid() (gid int)
//sysnb	Getrlimit(resource int, rlim *Rlimit) (err error)
//sysnb	Getuid() (uid int)
//sys	Ioperm(from int, num int, on int) (err error)
//sys	Iopl(level int) (err error)
//sys	Lchown(path string, uid int, gid int) (err error)
//sys	Listen(s int, n int) (err error)
//sys	Lstat(path string, stat *Stat_t) (err error)
//sys	Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64
//sys	Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64
//sys	Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK
//sys	Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error)
//sys	sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
//sys	Setfsgid(gid int) (err error)
//sys	Setfsuid(uid int) (err error)
//sysnb	Setgid(gid int) (err error)
//sysnb	Setregid(rgid int, egid int) (err error)
//sysnb	Setresgid(rgid int, egid int, sgid int) (err error)
//sysnb	Setresuid(ruid int, euid int, suid int) (err error)
//sysnb	Setrlimit(resource int, rlim *Rlimit) (err error)
//sysnb	Setreuid(ruid int, euid int) (err error)
//sys	Shutdown(fd int, how int) (err error)
//sys	Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error)
//sys	Stat(path string, stat *Stat_t) (err error)
//sys	Statfs(path string, buf *Statfs_t) (err error)
//sys	SyncFileRange(fd int, off int64, n int64, flags int) (err error)
//sys	Truncate(path string, length int64) (err error)
//sys	accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error)
//sys	accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error)
//sys	bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error)
//sys	connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error)
//sysnb	getgroups(n int, list *_Gid_t) (nn int, err error)
//sysnb	setgroups(n int, list *_Gid_t) (err error)
//sys	getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *_Socklen) (err error)
//sys	setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr) (err error)
//sysnb	socket(domain int, typ int, proto int) (fd int, err error)
//sysnb	socketpair(domain int, typ int, proto int, fd *[2]int32) (err error)
//sysnb	getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error)
//sysnb	getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error)
//sys	recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error)
//sys	sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (err error)
//sys	recvmsg(s int, msg *Msghdr, flags int) (n int, err error)
//sys	sendmsg(s int, msg *Msghdr, flags int) (n int, err error)
//sys	mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error)

func Getpagesize() int { return 4096 }

//go:noescape
func gettimeofday(tv *Timeval) (err Errno)

func Gettimeofday(tv *Timeval) (err error) {
	errno := gettimeofday(tv)
	if errno != 0 {
		return errno
	}
	return nil
}

func Time(t *Time_t) (tt Time_t, err error) {
	var tv Timeval
	errno := gettimeofday(&tv)
	if errno != 0 {
		return 0, errno
	}
	if t != nil {
		*t = Time_t(tv.Sec)
	}
	return Time_t(tv.Sec), nil
}

func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }

func NsecToTimespec(nsec int64) (ts Timespec) {
	ts.Sec = nsec / 1e9
	ts.Nsec = nsec % 1e9
	return
}

func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }

func NsecToTimeval(nsec int64) (tv Timeval) {
	nsec += 999 // round up to microsecond
	tv.Sec = nsec / 1e9
	tv.Usec = nsec % 1e9 / 1e3
	return
}

func (r *PtraceRegs) PC() uint64 { return r.Rip }

func (r *PtraceRegs) SetPC(pc uint64) { r.Rip = pc }

func (iov *Iovec) SetLen(length int) {
	iov.Len = uint64(length)
}

func (msghdr *Msghdr) SetControllen(length int) {
	msghdr.Controllen = uint64(length)
}

func (cmsg *Cmsghdr) SetLen(length int) {
	cmsg.Len = uint64(length)
}
<|endoftext|>


<commit_before>package cacher

import (
	"sync"
	"time"
)

type (
	CacheFiller func() (data interface{}, err error)

	Cacher struct {
		data map[string]*cacheItem
		mux  sync.Mutex
	}
)

// New returns a new cacher
// purgeEvery sets the timer for purging old cache, set to 0 to never purge.
func New(purgeEvery time.Duration) *Cacher {
	c := &Cacher{
		data: map[string]*cacheItem{},
	}
	if purgeEvery > 0 {
		go c.purge(purgeEvery)
	}
	return c
}

func (c *Cacher) Get(key string, fn CacheFiller, ttl time.Duration) (data interface{}, err error) {
	var ci *cacheItem
	c.mux.Lock()
	if ci = c.data[key]; ci == nil {
		ci = &cacheItem{
			fn:  fn,
			ttl: int64(ttl / time.Second),
		}
		c.data[key] = ci
	}
	c.mux.Unlock()

	return ci.call()
}

func (c *Cacher) Delete(key string) {
	c.mux.Lock()
	delete(c.data, key)
	c.mux.Unlock()
}

func (c *Cacher) purge(ttl time.Duration) {
	for {
		time.Sleep(ttl)
		ts := time.Now().Add(ttl).Unix()
		c.mux.Lock()
		for key, ci := range c.data {
			if ci.expiresAt > ts {
				delete(c.data, key)
			}
		}
		c.mux.Unlock()
	}
}

type cacheItem struct {
	fn        CacheFiller
	expiresAt int64
	ttl       int64

	data interface{}
	err  error

	sync.Mutex
}

func (ci *cacheItem) call() (data interface{}, err error) {
	ts := time.Now().Unix()
	ci.Lock()
	if ci.expiresAt == 0 || ci.expiresAt < ts {
		ci.data, ci.err = ci.fn()
		ci.expiresAt = ts + ci.ttl
	}
	data, err = ci.data, ci.err
	ci.Unlock()
	return
}
<commit_msg>oops fix purging logic<commit_after>package cacher

import (
	"sync"
	"time"
)

type (
	CacheFiller func() (data interface{}, err error)

	Cacher struct {
		data map[string]*cacheItem
		mux  sync.Mutex
	}
)

// New returns a new cacher
// purgeEvery sets the timer for purging old cache, set to 0 to never purge.
func New(purgeEvery time.Duration) *Cacher {
	c := &Cacher{
		data: map[string]*cacheItem{},
	}
	if purgeEvery > 0 {
		go c.purge(purgeEvery)
	}
	return c
}

func (c *Cacher) Get(key string, fn CacheFiller, ttl time.Duration) (data interface{}, err error) {
	var ci *cacheItem
	c.mux.Lock()
	if ci = c.data[key]; ci == nil {
		ci = &cacheItem{
			fn:  fn,
			ttl: int64(ttl / time.Second),
		}
		c.data[key] = ci
	}
	c.mux.Unlock()

	return ci.call()
}

func (c *Cacher) Delete(key string) {
	c.mux.Lock()
	delete(c.data, key)
	c.mux.Unlock()
}

func (c *Cacher) purge(ttl time.Duration) {
	for {
		time.Sleep(ttl)
		ts := time.Now().Unix()
		c.mux.Lock()
		for key, ci := range c.data {
			if ci.expiresAt < ts {
				delete(c.data, key)
			}
		}
		c.mux.Unlock()
	}
}

type cacheItem struct {
	fn        CacheFiller
	expiresAt int64
	ttl       int64

	data interface{}
	err  error

	sync.Mutex
}

func (ci *cacheItem) call() (data interface{}, err error) {
	ts := time.Now().Unix()
	ci.Lock()
	if ci.expiresAt == 0 || ci.expiresAt < ts {
		ci.data, ci.err = ci.fn()
		ci.expiresAt = ts + ci.ttl
	}
	data, err = ci.data, ci.err
	ci.Unlock()
	return
}
<|endoftext|>


<commit_before>// The MIT License (MIT)

// Copyright (c) 2016 Maciej Borzecki

// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package models

// item (such as rocket launcher, railgun, red armor) descriptor
type ItemDesc struct {
	// short name, ex. RA (red armor), RL (rocket launcher)
	Short string
	// long name - Rocket Launcher
	Name string
}

// descriptor of items, mainly for presentation purposes
type DescData struct {
	// mapping of short name to long names
	ItemMap map[string]ItemDesc
}

var (
	descData = &DescData{
		ItemMap: map[string]ItemDesc{
			"G":   ItemDesc{"G", "Gauntlet"},
			"MG":  ItemDesc{"MG", "Machine Gun"},
			"SG":  ItemDesc{"SG", "Shotgun"},
			"PG":  ItemDesc{"PG", "Plasma Gun"},
			"GL":  ItemDesc{"GL", "Grenade Launcher"},
			"RL":  ItemDesc{"RL", "Rocket Launcher"},
			"RG":  ItemDesc{"RG", "Railgun"},
			"LG":  ItemDesc{"LG", "Lightning Gun"},
			"BFG": ItemDesc{"BFG", "BFG"},
			"Q":   ItemDesc{"Q", "Quad Damage"},
			"MH":  ItemDesc{"MH", "Mega Health"},
			"BS":  ItemDesc{"BS", "Battle Suit"},
			"RA":  ItemDesc{"RA", "Red Armor"},
			"YA":  ItemDesc{"YA", "Yellow Armor"},
			"GA":  ItemDesc{"GA", "Green Armor"},
		},
	}
)

// obtain pointer to item descriptor
func GetItemDesc() *DescData {
	return descData
}

// returns true if effects of given item are time bounded
func ItemHasDuration(itype string) bool {
	switch itype {
	case "Q", "BS":
		return true
	}
	return false
}
<commit_msg>models/desc: add symbolic names and descriptions for all items<commit_after>// The MIT License (MIT)

// Copyright (c) 2016 Maciej Borzecki

// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package models

const (
	// for sake of simplicity define item types using names
	// matching data coming from CPMA Q3 server
	RedArmor     = "RA"
	YellowArmor  = "YA"
	GreenArmor   = "GA"
	MegaHealth   = "MH"
	QuadDamage   = "Quad"
	BattleSuit   = "BattleSuit"
	Regeneration = "Regen"
	Haste        = "Haste"
	Flight       = "Flight"
	Invisibility = "Invis"

	// weapons
	Gauntlet        = "G"
	Machinegun      = "MG"
	Shotgun         = "SG"
	Plasmagun       = "PG"
	GrenadeLauncher = "GL"
	RocketLauncher  = "RL"
	Railgun         = "RG"
	BFG             = "BFG"
	LightningGun    = "LG"
)

// item (such as rocket launcher, railgun, red armor) descriptor
type ItemDesc struct {
	// short name, ex. RA (red armor), RL (rocket launcher)
	Short string
	// long name - Rocket Launcher
	Name string
}

// descriptor of items, mainly for presentation purposes
type DescData struct {
	// mapping of short name to long names
	ItemMap map[string]ItemDesc
}

var (
	descData = &DescData{
		ItemMap: map[string]ItemDesc{
			// weapons
			Gauntlet:        ItemDesc{"G", "Gauntlet"},
			Machinegun:      ItemDesc{"MG", "Machine Gun"},
			Shotgun:         ItemDesc{"SG", "Shotgun"},
			Plasmagun:       ItemDesc{"PG", "Plasma Gun"},
			GrenadeLauncher: ItemDesc{"GL", "Grenade Launcher"},
			RocketLauncher:  ItemDesc{"RL", "Rocket Launcher"},
			Railgun:         ItemDesc{"RG", "Railgun"},
			LightningGun:    ItemDesc{"LG", "Lightning Gun"},
			BFG:             ItemDesc{"BFG", "BFG"},

			// items
			RedArmor:     ItemDesc{"RA", "Red Armor"},
			YellowArmor:  ItemDesc{"YA", "Yellow Armor"},
			GreenArmor:   ItemDesc{"GA", "Green Armor"},
			MegaHealth:   ItemDesc{"MH", "Mega Health"},
			QuadDamage:   ItemDesc{"Q", "Quad Damage"},
			BattleSuit:   ItemDesc{"BS", "Battle Suit"},
			Regeneration: ItemDesc{"REG", "Regeneration"},
			Haste:        ItemDesc{"HS", "Haste"},
			Flight:       ItemDesc{"FL", "Flight"},
			Invisibility: ItemDesc{"INV", "Invisibility"},
		},
	}
)

// obtain pointer to item descriptor
func GetItemDesc() *DescData {
	return descData
}

// returns true if effects of given item are time bounded
func ItemHasDuration(itype string) bool {
	switch itype {
	case "Q", "BS":
		return true
	}
	return false
}
<|endoftext|>